From dbd4452de4f4054efb59e244b476eb5158ea5ffd Mon Sep 17 00:00:00 2001 From: tom goriunov Date: Fri, 29 Sep 2023 10:45:23 -0300 Subject: [PATCH] workaround for ENV replacement script (#1212) * base implementation of client script with envs values * better ENVs setup for pw tests * fix tests --- .gitignore | 1 + Dockerfile | 13 +++---- configs/app/api.ts | 10 +++--- configs/app/app.ts | 8 ++--- configs/app/chain.ts | 20 +++++------ configs/app/features/account.ts | 8 ++--- configs/app/features/addressVerification.ts | 2 +- configs/app/features/adsBanner.ts | 6 ++-- configs/app/features/adsText.ts | 2 +- configs/app/features/beaconChain.ts | 6 ++-- configs/app/features/blockchainInteraction.ts | 2 +- configs/app/features/googleAnalytics.ts | 2 +- configs/app/features/graphqlApiDocs.ts | 2 +- configs/app/features/marketplace.ts | 4 +-- configs/app/features/mixpanel.ts | 2 +- configs/app/features/restApiDocs.ts | 2 +- configs/app/features/rollup.ts | 6 ++-- configs/app/features/sentry.ts | 8 ++--- configs/app/features/sol2uml.ts | 2 +- configs/app/features/stats.ts | 2 +- configs/app/features/verifiedTokens.ts | 2 +- configs/app/features/web3Wallet.ts | 4 +-- configs/app/meta.ts | 6 ++-- configs/app/services.ts | 2 +- configs/app/ui.ts | 32 ++++++++--------- configs/app/ui/views/address.ts | 2 +- configs/app/ui/views/block.ts | 2 +- configs/app/utils.ts | 27 +++++++++++--- configs/envs/.env.pw | 14 -------- ...{make_envs_template.sh => collect_envs.sh} | 12 +++---- deploy/scripts/entrypoint.sh | 12 +++---- deploy/scripts/make_envs_script.sh | 29 +++++++++++++++ deploy/scripts/replace_envs.sh | 36 ------------------- deploy/scripts/validate_envs.sh | 13 +++++++ deploy/tools/envs-validator/.gitignore | 2 +- deploy/tools/envs-validator/dev.sh | 2 +- deploy/tools/envs-validator/index.ts | 8 ++--- deploy/tools/envs-validator/tsconfig.json | 2 +- deploy/tools/feature-reporter/tsconfig.json | 2 +- global.d.ts | 1 + jest/setup.ts | 9 +++++ package.json | 6 ++-- pages/_document.tsx | 3 ++ playwright-ct.config.ts | 2 +- playwright/index.html | 2 +- playwright/make-envs-script.sh | 33 ----------------- tools/scripts/dev.preset.sh | 6 ++-- .../scripts/pw.docker.sh | 8 +++-- tools/scripts/pw.sh | 14 ++++++++ ui/pages/SearchResults.pw.tsx | 4 +-- ui/snippets/footer/Footer.pw.tsx | 4 +-- ui/snippets/header/Burger.pw.tsx | 4 +-- .../navigation/NavigationDesktop.pw.tsx | 4 +-- ui/snippets/networkMenu/NetworkLogo.pw.tsx | 14 ++++---- ui/snippets/networkMenu/NetworkMenu.pw.tsx | 4 +-- ui/snippets/searchBar/SearchBar.pw.tsx | 4 +-- 56 files changed, 223 insertions(+), 216 deletions(-) rename deploy/scripts/{make_envs_template.sh => collect_envs.sh} (72%) create mode 100755 deploy/scripts/make_envs_script.sh delete mode 100755 deploy/scripts/replace_envs.sh create mode 100755 deploy/scripts/validate_envs.sh delete mode 100755 playwright/make-envs-script.sh rename playwright/run-tests.sh => tools/scripts/pw.docker.sh (63%) create mode 100755 tools/scripts/pw.sh diff --git a/.gitignore b/.gitignore index 931c8dc622..b90e088f1a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /.next/ /out/ /public/assets/ +/public/envs.js # production /build diff --git a/Dockerfile b/Dockerfile index 5674d28f3a..38256d691d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,9 +47,9 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . -# Generate .env.production with ENVs placeholders and save build args into .env file -COPY --chmod=+x ./deploy/scripts/make_envs_template.sh ./ -RUN ./make_envs_template.sh ./docs/ENVS.md +# Generate .env.registry with ENVs list and save build args into .env file +COPY --chmod=+x ./deploy/scripts/collect_envs.sh ./ +RUN ./collect_envs.sh ./docs/ENVS.md # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry @@ -98,8 +98,9 @@ COPY --from=builder /app/deploy/tools/feature-reporter/index.js ./feature-report # Copy scripts ## Entripoint COPY --chmod=+x ./deploy/scripts/entrypoint.sh . -## ENV replacer -COPY --chmod=+x ./deploy/scripts/replace_envs.sh . +## ENV validator and client script maker +COPY --chmod=+x ./deploy/scripts/validate_envs.sh . +COPY --chmod=+x ./deploy/scripts/make_envs_script.sh . ## Assets downloader COPY --chmod=+x ./deploy/scripts/download_assets.sh . ## Favicon generator @@ -109,7 +110,7 @@ RUN ["chmod", "-R", "777", "./deploy/tools/favicon-generator"] RUN ["chmod", "-R", "777", "./public"] # Copy ENVs files -COPY --from=builder /app/.env.production . +COPY --from=builder /app/.env.registry . COPY --from=builder /app/.env . # Automatically leverage output traces to reduce image size diff --git a/configs/app/api.ts b/configs/app/api.ts index f8e6512f8c..1f834eee17 100644 --- a/configs/app/api.ts +++ b/configs/app/api.ts @@ -2,9 +2,9 @@ import stripTrailingSlash from 'lib/stripTrailingSlash'; import { getEnvValue } from './utils'; -const apiHost = getEnvValue(process.env.NEXT_PUBLIC_API_HOST); -const apiSchema = getEnvValue(process.env.NEXT_PUBLIC_API_PROTOCOL) || 'https'; -const apiPort = getEnvValue(process.env.NEXT_PUBLIC_API_PORT); +const apiHost = getEnvValue('NEXT_PUBLIC_API_HOST'); +const apiSchema = getEnvValue('NEXT_PUBLIC_API_PROTOCOL') || 'https'; +const apiPort = getEnvValue('NEXT_PUBLIC_API_PORT'); const apiEndpoint = [ apiSchema || 'https', '://', @@ -12,7 +12,7 @@ const apiEndpoint = [ apiPort && ':' + apiPort, ].filter(Boolean).join(''); -const socketSchema = getEnvValue(process.env.NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL) || 'wss'; +const socketSchema = getEnvValue('NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL') || 'wss'; const socketEndpoint = [ socketSchema, '://', @@ -24,7 +24,7 @@ const api = Object.freeze({ host: apiHost, endpoint: apiEndpoint, socket: socketEndpoint, - basePath: stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_API_BASE_PATH) || ''), + basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_API_BASE_PATH') || ''), }); export default api; diff --git a/configs/app/app.ts b/configs/app/app.ts index 04d8c598b0..be9a703c6b 100644 --- a/configs/app/app.ts +++ b/configs/app/app.ts @@ -1,8 +1,8 @@ import { getEnvValue } from './utils'; -const appPort = getEnvValue(process.env.NEXT_PUBLIC_APP_PORT); -const appSchema = getEnvValue(process.env.NEXT_PUBLIC_APP_PROTOCOL); -const appHost = getEnvValue(process.env.NEXT_PUBLIC_APP_HOST); +const appPort = getEnvValue('NEXT_PUBLIC_APP_PORT'); +const appSchema = getEnvValue('NEXT_PUBLIC_APP_PROTOCOL'); +const appHost = getEnvValue('NEXT_PUBLIC_APP_HOST'); const baseUrl = [ appSchema || 'https', '://', @@ -17,7 +17,7 @@ const app = Object.freeze({ host: appHost, port: appPort, baseUrl, - useProxy: getEnvValue(process.env.NEXT_PUBLIC_USE_NEXT_JS_PROXY) === 'true', + useProxy: getEnvValue('NEXT_PUBLIC_USE_NEXT_JS_PROXY') === 'true', }); export default app; diff --git a/configs/app/chain.ts b/configs/app/chain.ts index 76525d309d..b65fbeb384 100644 --- a/configs/app/chain.ts +++ b/configs/app/chain.ts @@ -3,20 +3,20 @@ import { getEnvValue } from './utils'; const DEFAULT_CURRENCY_DECIMALS = 18; const chain = Object.freeze({ - id: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ID), - name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_NAME), - shortName: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_SHORT_NAME), + id: getEnvValue('NEXT_PUBLIC_NETWORK_ID'), + name: getEnvValue('NEXT_PUBLIC_NETWORK_NAME'), + shortName: getEnvValue('NEXT_PUBLIC_NETWORK_SHORT_NAME'), currency: { - name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_NAME), - symbol: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL), - decimals: Number(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS)) || DEFAULT_CURRENCY_DECIMALS, + name: getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_NAME'), + symbol: getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL'), + decimals: Number(getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS')) || DEFAULT_CURRENCY_DECIMALS, }, governanceToken: { - symbol: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL), + symbol: getEnvValue('NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL'), }, - rpcUrl: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_RPC_URL), - isTestnet: getEnvValue(process.env.NEXT_PUBLIC_IS_TESTNET) === 'true', - verificationType: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE) || 'mining', + rpcUrl: getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL'), + isTestnet: getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true', + verificationType: getEnvValue('NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE') || 'mining', }); export default chain; diff --git a/configs/app/features/account.ts b/configs/app/features/account.ts index 794241d088..120a94b7e9 100644 --- a/configs/app/features/account.ts +++ b/configs/app/features/account.ts @@ -5,12 +5,12 @@ import stripTrailingSlash from 'lib/stripTrailingSlash'; import app from '../app'; import { getEnvValue } from '../utils'; -const authUrl = stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_AUTH_URL) || app.baseUrl); +const authUrl = stripTrailingSlash(getEnvValue('NEXT_PUBLIC_AUTH_URL') || app.baseUrl); const logoutUrl = (() => { try { - const envUrl = getEnvValue(process.env.NEXT_PUBLIC_LOGOUT_URL); - const auth0ClientId = getEnvValue(process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID); + const envUrl = getEnvValue('NEXT_PUBLIC_LOGOUT_URL'); + const auth0ClientId = getEnvValue('NEXT_PUBLIC_AUTH0_CLIENT_ID'); const returnUrl = authUrl + '/auth/logout'; if (!envUrl || !auth0ClientId) { @@ -31,7 +31,7 @@ const title = 'My account'; const config: Feature<{ authUrl: string; logoutUrl: string }> = (() => { if ( - getEnvValue(process.env.NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED) === 'true' && + getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' && authUrl && logoutUrl ) { diff --git a/configs/app/features/addressVerification.ts b/configs/app/features/addressVerification.ts index d1ef8d1c9a..f5f6cf4cf3 100644 --- a/configs/app/features/addressVerification.ts +++ b/configs/app/features/addressVerification.ts @@ -4,7 +4,7 @@ import { getEnvValue } from '../utils'; import account from './account'; import verifiedTokens from './verifiedTokens'; -const adminServiceApiHost = getEnvValue(process.env.NEXT_PUBLIC_ADMIN_SERVICE_API_HOST); +const adminServiceApiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST'); const title = 'Address verification in "My account"'; diff --git a/configs/app/features/adsBanner.ts b/configs/app/features/adsBanner.ts index 2408fbb173..0785ab160f 100644 --- a/configs/app/features/adsBanner.ts +++ b/configs/app/features/adsBanner.ts @@ -6,7 +6,7 @@ import type { AdBannerProviders } from 'types/client/adProviders'; import { getEnvValue, parseEnvJson } from '../utils'; const provider: AdBannerProviders = (() => { - const envValue = getEnvValue(process.env.NEXT_PUBLIC_AD_BANNER_PROVIDER) as AdBannerProviders; + const envValue = getEnvValue('NEXT_PUBLIC_AD_BANNER_PROVIDER') as AdBannerProviders; return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise'; })(); @@ -27,8 +27,8 @@ type AdsBannerFeaturePayload = { const config: Feature = (() => { if (provider === 'adbutler') { - const desktopConfig = parseEnvJson(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP)); - const mobileConfig = parseEnvJson(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE)); + const desktopConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP')); + const mobileConfig = parseEnvJson(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE')); if (desktopConfig && mobileConfig) { return Object.freeze({ diff --git a/configs/app/features/adsText.ts b/configs/app/features/adsText.ts index 297378e76b..b44fa59e85 100644 --- a/configs/app/features/adsText.ts +++ b/configs/app/features/adsText.ts @@ -5,7 +5,7 @@ import type { AdTextProviders } from 'types/client/adProviders'; import { getEnvValue } from '../utils'; const provider: AdTextProviders = (() => { - const envValue = getEnvValue(process.env.NEXT_PUBLIC_AD_TEXT_PROVIDER) as AdTextProviders; + const envValue = getEnvValue('NEXT_PUBLIC_AD_TEXT_PROVIDER') as AdTextProviders; return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'coinzilla'; })(); diff --git a/configs/app/features/beaconChain.ts b/configs/app/features/beaconChain.ts index 80b7bd0fa0..a29540b756 100644 --- a/configs/app/features/beaconChain.ts +++ b/configs/app/features/beaconChain.ts @@ -5,14 +5,14 @@ import { getEnvValue } from '../utils'; const title = 'Beacon chain'; const config: Feature<{ currency: { symbol: string } }> = (() => { - if (getEnvValue(process.env.NEXT_PUBLIC_HAS_BEACON_CHAIN) === 'true') { + if (getEnvValue('NEXT_PUBLIC_HAS_BEACON_CHAIN') === 'true') { return Object.freeze({ title, isEnabled: true, currency: { symbol: - getEnvValue(process.env.NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL) || - getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL) || + getEnvValue('NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL') || + getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL') || '', // maybe we need some other default value here }, }); diff --git a/configs/app/features/blockchainInteraction.ts b/configs/app/features/blockchainInteraction.ts index 68aa12c0f3..788e059ec9 100644 --- a/configs/app/features/blockchainInteraction.ts +++ b/configs/app/features/blockchainInteraction.ts @@ -3,7 +3,7 @@ import type { Feature } from './types'; import chain from '../chain'; import { getEnvValue } from '../utils'; -const walletConnectProjectId = getEnvValue(process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID); +const walletConnectProjectId = getEnvValue('NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID'); const title = 'Blockchain interaction (writing to contract, etc.)'; diff --git a/configs/app/features/googleAnalytics.ts b/configs/app/features/googleAnalytics.ts index 63eec4aa16..4fe9a9bf9e 100644 --- a/configs/app/features/googleAnalytics.ts +++ b/configs/app/features/googleAnalytics.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const propertyId = getEnvValue(process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID); +const propertyId = getEnvValue('NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID'); const title = 'Google analytics'; diff --git a/configs/app/features/graphqlApiDocs.ts b/configs/app/features/graphqlApiDocs.ts index d9a8a199f3..09285ac898 100644 --- a/configs/app/features/graphqlApiDocs.ts +++ b/configs/app/features/graphqlApiDocs.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const defaultTxHash = getEnvValue(process.env.NEXT_PUBLIC_GRAPHIQL_TRANSACTION); +const defaultTxHash = getEnvValue('NEXT_PUBLIC_GRAPHIQL_TRANSACTION'); const title = 'GraphQL API documentation'; diff --git a/configs/app/features/marketplace.ts b/configs/app/features/marketplace.ts index b2b43275e8..5ea76352f2 100644 --- a/configs/app/features/marketplace.ts +++ b/configs/app/features/marketplace.ts @@ -4,8 +4,8 @@ import chain from '../chain'; import { getEnvValue, getExternalAssetFilePath } from '../utils'; // config file will be downloaded at run-time and saved in the public folder -const configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', process.env.NEXT_PUBLIC_MARKETPLACE_CONFIG_URL); -const submitFormUrl = getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM); +const configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL'); +const submitFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM'); const title = 'Marketplace'; diff --git a/configs/app/features/mixpanel.ts b/configs/app/features/mixpanel.ts index 66c73ac18b..ef9fabd91f 100644 --- a/configs/app/features/mixpanel.ts +++ b/configs/app/features/mixpanel.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const projectToken = getEnvValue(process.env.NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN); +const projectToken = getEnvValue('NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN'); const title = 'Mixpanel analytics'; diff --git a/configs/app/features/restApiDocs.ts b/configs/app/features/restApiDocs.ts index 5c9e3b57f2..281282ab05 100644 --- a/configs/app/features/restApiDocs.ts +++ b/configs/app/features/restApiDocs.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const specUrl = getEnvValue(process.env.NEXT_PUBLIC_API_SPEC_URL) || `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml`; +const specUrl = getEnvValue('NEXT_PUBLIC_API_SPEC_URL') || `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml`; const title = 'REST API documentation'; diff --git a/configs/app/features/rollup.ts b/configs/app/features/rollup.ts index 18362d82b0..76954ce904 100644 --- a/configs/app/features/rollup.ts +++ b/configs/app/features/rollup.ts @@ -5,11 +5,11 @@ import { getEnvValue } from '../utils'; const title = 'Rollup (L2) chain'; const config: Feature<{ L1BaseUrl: string; withdrawalUrl: string }> = (() => { - const L1BaseUrl = getEnvValue(process.env.NEXT_PUBLIC_L1_BASE_URL); - const withdrawalUrl = getEnvValue(process.env.NEXT_PUBLIC_L2_WITHDRAWAL_URL); + const L1BaseUrl = getEnvValue('NEXT_PUBLIC_L1_BASE_URL'); + const withdrawalUrl = getEnvValue('NEXT_PUBLIC_L2_WITHDRAWAL_URL'); if ( - getEnvValue(process.env.NEXT_PUBLIC_IS_L2_NETWORK) === 'true' && + getEnvValue('NEXT_PUBLIC_IS_L2_NETWORK') === 'true' && L1BaseUrl && withdrawalUrl ) { diff --git a/configs/app/features/sentry.ts b/configs/app/features/sentry.ts index e66411639e..1e5b0a055c 100644 --- a/configs/app/features/sentry.ts +++ b/configs/app/features/sentry.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const dsn = getEnvValue(process.env.NEXT_PUBLIC_SENTRY_DSN); +const dsn = getEnvValue('NEXT_PUBLIC_SENTRY_DSN'); const title = 'Sentry error monitoring'; @@ -12,9 +12,9 @@ const config: Feature<{ dsn: string; environment: string | undefined; cspReportU title, isEnabled: true, dsn, - environment: getEnvValue(process.env.NEXT_PUBLIC_APP_ENV) || getEnvValue(process.env.NODE_ENV), - cspReportUrl: getEnvValue(process.env.SENTRY_CSP_REPORT_URI), - instance: getEnvValue(process.env.NEXT_PUBLIC_APP_INSTANCE), + environment: getEnvValue('NEXT_PUBLIC_APP_ENV') || getEnvValue('NODE_ENV'), + cspReportUrl: getEnvValue('SENTRY_CSP_REPORT_URI'), + instance: getEnvValue('NEXT_PUBLIC_APP_INSTANCE'), }); } diff --git a/configs/app/features/sol2uml.ts b/configs/app/features/sol2uml.ts index 8743520eb5..06853e62d8 100644 --- a/configs/app/features/sol2uml.ts +++ b/configs/app/features/sol2uml.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const apiEndpoint = getEnvValue(process.env.NEXT_PUBLIC_VISUALIZE_API_HOST); +const apiEndpoint = getEnvValue('NEXT_PUBLIC_VISUALIZE_API_HOST'); const title = 'Solidity to UML diagrams'; diff --git a/configs/app/features/stats.ts b/configs/app/features/stats.ts index e901f8eaf3..b05f2f9659 100644 --- a/configs/app/features/stats.ts +++ b/configs/app/features/stats.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const apiEndpoint = getEnvValue(process.env.NEXT_PUBLIC_STATS_API_HOST); +const apiEndpoint = getEnvValue('NEXT_PUBLIC_STATS_API_HOST'); const title = 'Blockchain statistics'; diff --git a/configs/app/features/verifiedTokens.ts b/configs/app/features/verifiedTokens.ts index 30d67ce048..521851d295 100644 --- a/configs/app/features/verifiedTokens.ts +++ b/configs/app/features/verifiedTokens.ts @@ -2,7 +2,7 @@ import type { Feature } from './types'; import { getEnvValue } from '../utils'; -const contractInfoApiHost = getEnvValue(process.env.NEXT_PUBLIC_CONTRACT_INFO_API_HOST); +const contractInfoApiHost = getEnvValue('NEXT_PUBLIC_CONTRACT_INFO_API_HOST'); const title = 'Verified tokens info'; diff --git a/configs/app/features/web3Wallet.ts b/configs/app/features/web3Wallet.ts index 6b7cf88f1c..f4df4d56ea 100644 --- a/configs/app/features/web3Wallet.ts +++ b/configs/app/features/web3Wallet.ts @@ -5,7 +5,7 @@ import type { WalletType } from 'types/client/wallets'; import { getEnvValue, parseEnvJson } from '../utils'; const wallets = ((): Array | undefined => { - const envValue = getEnvValue(process.env.NEXT_PUBLIC_WEB3_WALLETS); + const envValue = getEnvValue('NEXT_PUBLIC_WEB3_WALLETS'); if (envValue === 'none') { return; } @@ -28,7 +28,7 @@ const config: Feature<{ wallets: Array; addToken: { isDisabled: bool isEnabled: true, wallets, addToken: { - isDisabled: getEnvValue(process.env.NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET) === 'true', + isDisabled: getEnvValue('NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET') === 'true', }, addNetwork: {}, }); diff --git a/configs/app/meta.ts b/configs/app/meta.ts index db601cd25e..cf0f309534 100644 --- a/configs/app/meta.ts +++ b/configs/app/meta.ts @@ -4,10 +4,10 @@ import { getEnvValue, getExternalAssetFilePath } from './utils'; const defaultImageUrl = app.baseUrl + '/static/og_placeholder.png'; const meta = Object.freeze({ - promoteBlockscoutInTitle: getEnvValue(process.env.NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE) || 'true', + promoteBlockscoutInTitle: getEnvValue('NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE') || 'true', og: { - description: getEnvValue(process.env.NEXT_PUBLIC_OG_DESCRIPTION) || '', - imageUrl: getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL', process.env.NEXT_PUBLIC_OG_IMAGE_URL) || defaultImageUrl, + description: getEnvValue('NEXT_PUBLIC_OG_DESCRIPTION') || '', + imageUrl: getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL') || defaultImageUrl, }, }); diff --git a/configs/app/services.ts b/configs/app/services.ts index a6249f611f..86df538215 100644 --- a/configs/app/services.ts +++ b/configs/app/services.ts @@ -2,6 +2,6 @@ import { getEnvValue } from './utils'; export default Object.freeze({ reCaptcha: { - siteKey: getEnvValue(process.env.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY), + siteKey: getEnvValue('NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY'), }, }); diff --git a/configs/app/ui.ts b/configs/app/ui.ts index 4e72fa16f2..ebae8c805c 100644 --- a/configs/app/ui.ts +++ b/configs/app/ui.ts @@ -11,36 +11,36 @@ const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% const UI = Object.freeze({ sidebar: { logo: { - 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', process.env.NEXT_PUBLIC_NETWORK_LOGO), - dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', process.env.NEXT_PUBLIC_NETWORK_LOGO_DARK), + 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO'), + dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK'), }, icon: { - 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', process.env.NEXT_PUBLIC_NETWORK_ICON), - dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', process.env.NEXT_PUBLIC_NETWORK_ICON_DARK), + 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON'), + dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK'), }, - otherLinks: parseEnvJson>(getEnvValue(process.env.NEXT_PUBLIC_OTHER_LINKS)) || [], - featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', process.env.NEXT_PUBLIC_FEATURED_NETWORKS), + otherLinks: parseEnvJson>(getEnvValue('NEXT_PUBLIC_OTHER_LINKS')) || [], + featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS'), }, footer: { - links: getExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', process.env.NEXT_PUBLIC_FOOTER_LINKS), - frontendVersion: getEnvValue(process.env.NEXT_PUBLIC_GIT_TAG), - frontendCommit: getEnvValue(process.env.NEXT_PUBLIC_GIT_COMMIT_SHA), + links: getExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS'), + frontendVersion: getEnvValue('NEXT_PUBLIC_GIT_TAG'), + frontendCommit: getEnvValue('NEXT_PUBLIC_GIT_COMMIT_SHA'), }, homepage: { - charts: parseEnvJson>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [], + charts: parseEnvJson>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [], plate: { - background: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND) || HOMEPAGE_PLATE_BACKGROUND_DEFAULT, - textColor: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR) || 'white', + background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND') || HOMEPAGE_PLATE_BACKGROUND_DEFAULT, + textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR') || 'white', }, - showGasTracker: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER) === 'false' ? false : true, - showAvgBlockTime: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME) === 'false' ? false : true, + showGasTracker: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER') === 'false' ? false : true, + showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true, }, views, indexingAlert: { - isHidden: getEnvValue(process.env.NEXT_PUBLIC_HIDE_INDEXING_ALERT), + isHidden: getEnvValue('NEXT_PUBLIC_HIDE_INDEXING_ALERT'), }, explorers: { - items: parseEnvJson>(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_EXPLORERS)) || [], + items: parseEnvJson>(getEnvValue('NEXT_PUBLIC_NETWORK_EXPLORERS')) || [], }, }); diff --git a/configs/app/ui/views/address.ts b/configs/app/ui/views/address.ts index ee6089a274..b121ecfde5 100644 --- a/configs/app/ui/views/address.ts +++ b/configs/app/ui/views/address.ts @@ -4,7 +4,7 @@ import { IDENTICON_TYPES } from 'types/views/address'; import { getEnvValue } from 'configs/app/utils'; const identiconType: IdenticonType = (() => { - const value = getEnvValue(process.env.NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE); + const value = getEnvValue('NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE'); return IDENTICON_TYPES.find((type) => value === type) || 'jazzicon'; })(); diff --git a/configs/app/ui/views/block.ts b/configs/app/ui/views/block.ts index aba1a74666..5e42136a42 100644 --- a/configs/app/ui/views/block.ts +++ b/configs/app/ui/views/block.ts @@ -4,7 +4,7 @@ import { BLOCK_FIELDS_IDS } from 'types/views/block'; import { getEnvValue, parseEnvJson } from 'configs/app/utils'; const blockHiddenFields = (() => { - const parsedValue = parseEnvJson>(getEnvValue(process.env.NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS)) || []; + const parsedValue = parseEnvJson>(getEnvValue('NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS')) || []; if (!Array.isArray(parsedValue)) { return undefined; diff --git a/configs/app/utils.ts b/configs/app/utils.ts index da4dfa085a..4b95117242 100644 --- a/configs/app/utils.ts +++ b/configs/app/utils.ts @@ -1,6 +1,19 @@ +import isBrowser from 'lib/isBrowser'; import * as regexp from 'lib/regexp'; -export const getEnvValue = (env: T | undefined): T | undefined => env?.replaceAll('\'', '"') as T; +export const getEnvValue = (envName: string) => { + const envs = isBrowser() ? window.__envs : process.env; + + if (isBrowser() && envs.NEXT_PUBLIC_APP_INSTANCE === 'pw') { + const storageValue = localStorage.getItem(envName); + + if (storageValue) { + return storageValue; + } + } + + return envs[envName]?.replaceAll('\'', '"'); +}; export const parseEnvJson = (env: string | undefined): DataType | null => { try { @@ -10,15 +23,19 @@ export const parseEnvJson = (env: string | undefined): DataType | null } }; -export const getExternalAssetFilePath = (envName: string, envValue: string | undefined) => { - const parsedValue = getEnvValue(envValue); +export const getExternalAssetFilePath = (envName: string) => { + const parsedValue = getEnvValue(envName); if (!parsedValue) { return; } - const fileName = envName.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase(); - const fileExtension = parsedValue.match(regexp.FILE_EXTENSION)?.[1]; + return buildExternalAssetFilePath(envName, parsedValue); +}; + +export const buildExternalAssetFilePath = (name: string, value: string) => { + const fileName = name.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase(); + const fileExtension = value.match(regexp.FILE_EXTENSION)?.[1]; return `/assets/${ fileName }.${ fileExtension }`; }; diff --git a/configs/envs/.env.pw b/configs/envs/.env.pw index 2f7eedc067..3c66d7fde9 100644 --- a/configs/envs/.env.pw +++ b/configs/envs/.env.pw @@ -1,7 +1,4 @@ # Set of ENVs for Playwright components tests -# -# be aware that ALL ENVs should be present in order to make a correct variables list for the browser -# leave empty values for the ones that are not required # app configuration NEXT_PUBLIC_APP_PROTOCOL=http @@ -29,18 +26,10 @@ NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true -NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND= ## sidebar -NEXT_PUBLIC_FEATURED_NETWORKS= -NEXT_PUBLIC_NETWORK_LOGO= -NEXT_PUBLIC_NETWORK_LOGO_DARK= -NEXT_PUBLIC_NETWORK_ICON= -NEXT_PUBLIC_NETWORK_ICON_DARK= ## footer NEXT_PUBLIC_GIT_TAG=v1.0.11 -NEXT_PUBLIC_FOOTER_LINKS= ## views -NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS= ## misc NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}] @@ -50,8 +39,6 @@ NEXT_PUBLIC_APP_INSTANCE=pw NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form NEXT_PUBLIC_IS_L2_NETWORK=false -NEXT_PUBLIC_L1_BASE_URL= -NEXT_PUBLIC_L2_WITHDRAWAL_URL= NEXT_PUBLIC_AD_BANNER_PROVIDER=slise NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_AUTH_URL=http://localhost:3100 @@ -61,4 +48,3 @@ NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx -NEXT_PUBLIC_HAS_BEACON_CHAIN= diff --git a/deploy/scripts/make_envs_template.sh b/deploy/scripts/collect_envs.sh similarity index 72% rename from deploy/scripts/make_envs_template.sh rename to deploy/scripts/collect_envs.sh index 063e833a7d..d53dc989e4 100755 --- a/deploy/scripts/make_envs_template.sh +++ b/deploy/scripts/collect_envs.sh @@ -9,11 +9,11 @@ fi input_file="$1" prefix="NEXT_PUBLIC_" -# Function to make the environment variables template file +# Function to make the environment variables registry file based on documentation file ENVS.md # It will read the input file, extract all prefixed string and use them as variables names -# This variables will have placeholders for their values at buildtime which will be replaced with actual values at runtime -make_envs_template_file() { - output_file=".env.production" +# This variables will have dummy values assigned to them +make_registry_file() { + output_file=".env.registry" # Check if file already exists and empty its content if it does if [ -f "$output_file" ]; then @@ -21,7 +21,7 @@ make_envs_template_file() { fi grep -oE "${prefix}[[:alnum:]_]+" "$input_file" | sort -u | while IFS= read -r var_name; do - echo "$var_name=__PLACEHOLDER_FOR_${var_name}__" >> "$output_file" + echo "$var_name=__" >> "$output_file" done } @@ -41,5 +41,5 @@ save_build-time_envs() { done } -make_envs_template_file +make_registry_file save_build-time_envs \ No newline at end of file diff --git a/deploy/scripts/entrypoint.sh b/deploy/scripts/entrypoint.sh index 6867778018..2924f09189 100755 --- a/deploy/scripts/entrypoint.sh +++ b/deploy/scripts/entrypoint.sh @@ -3,10 +3,10 @@ # Download external assets ./download_assets.sh ./public/assets -# Check run-time ENVs values integrity -node "$(dirname "$0")/envs-validator.js" "$input" -if [ $? != 0 ]; then - exit 1 +# Check run-time ENVs values +./validate_envs.sh +if [ $? -ne 0 ]; then + exit 1 fi # Generate favicons bundle @@ -18,8 +18,8 @@ else fi echo -# Execute script for replace build-time ENVs placeholders with their values at runtime -./replace_envs.sh +# Create envs.js file with run-time environment variables for the client app +./make_envs_script.sh # Print list of enabled features node ./feature-reporter.js diff --git a/deploy/scripts/make_envs_script.sh b/deploy/scripts/make_envs_script.sh new file mode 100755 index 0000000000..a6029dc513 --- /dev/null +++ b/deploy/scripts/make_envs_script.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo "🌀 Creating client script with ENV values..." + +# Define the output file name +output_file="./public/envs.js" + +touch $output_file; +truncate -s 0 $output_file; + +# Check if the .env file exists and load ENVs from it +if [ -f .env ]; then + source .env +fi + +echo "window.__envs = {" >> $output_file; + +# Iterate through all environment variables +for var in $(env | grep '^NEXT_PUBLIC_' | cut -d= -f1); do + # Get the value of the variable + value="${!var}" + + # Write the variable name and value to the output file + echo "${var}: \"${value}\"," >> "$output_file" +done + +echo "}" >> $output_file; + +echo "✅ Done." diff --git a/deploy/scripts/replace_envs.sh b/deploy/scripts/replace_envs.sh deleted file mode 100755 index 0cb022d535..0000000000 --- a/deploy/scripts/replace_envs.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# no verbose -set +x - -# config -envFilename='.env.production' -nextFolder='./.next/' - -# replacing build-stage ENVs with run-stage ENVs -# https://raphaelpralat.medium.com/system-environment-variables-in-next-js-with-docker-1f0754e04cde -function replace_envs { - # read all config file - while read line; do - # no comment or not empty - if [ "${line:0:1}" == "#" ] || [ "${line}" == "" ]; then - continue - fi - - # split - configName="$(cut -d'=' -f1 <<<"$line")" - configValue="$(cut -d'=' -f2- <<<"$line")" - # get system env - envValue=$(env | grep "^$configName=" | sed "s/^$configName=//g"); - - # if config found - if [ -n "$configValue" ]; then - # replace all - echo "Replace: ${configValue} with: ${envValue}" - find $nextFolder \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#$configValue#${envValue-''}#g" - fi - done < $envFilename -} - -echo ⏳ Replacing build-stage ENV placholders with their run-time values... -replace_envs -echo 👍 Done! \ No newline at end of file diff --git a/deploy/scripts/validate_envs.sh b/deploy/scripts/validate_envs.sh new file mode 100755 index 0000000000..55211d1c59 --- /dev/null +++ b/deploy/scripts/validate_envs.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Check if the .env file exists +if [ -f .env ]; then + # Load the environment variables from .env + source .env +fi + +# Check run-time ENVs values integrity +node "$(dirname "$0")/envs-validator.js" "$input" +if [ $? != 0 ]; then + exit 1 +fi \ No newline at end of file diff --git a/deploy/tools/envs-validator/.gitignore b/deploy/tools/envs-validator/.gitignore index eb0be25ccf..99976e7005 100644 --- a/deploy/tools/envs-validator/.gitignore +++ b/deploy/tools/envs-validator/.gitignore @@ -1,5 +1,5 @@ /node_modules /public .env -.env.production +.env.registry index.js \ No newline at end of file diff --git a/deploy/tools/envs-validator/dev.sh b/deploy/tools/envs-validator/dev.sh index db892e7fd4..f9f9175098 100755 --- a/deploy/tools/envs-validator/dev.sh +++ b/deploy/tools/envs-validator/dev.sh @@ -2,7 +2,7 @@ export NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) export NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0) -../../scripts/make_envs_template.sh ../../../docs/ENVS.md +../../scripts/collect_envs.sh ../../../docs/ENVS.md yarn build diff --git a/deploy/tools/envs-validator/index.ts b/deploy/tools/envs-validator/index.ts index 16f7dcf1e4..405f4b9178 100644 --- a/deploy/tools/envs-validator/index.ts +++ b/deploy/tools/envs-validator/index.ts @@ -81,17 +81,17 @@ async function getExternalJsonContent(fileName: string, envValue: string): Promi }); } -async function checkPlaceholdersCongruity(runTimeEnvs: Record) { +async function checkPlaceholdersCongruity(envsMap: Record) { try { console.log(`🌀 Checking environment variables and their placeholders congruity...`); - const placeholders = await getEnvsPlaceholders(path.resolve(__dirname, '.env.production')); + const runTimeEnvs = await getEnvsPlaceholders(path.resolve(__dirname, '.env.registry')); const buildTimeEnvs = await getEnvsPlaceholders(path.resolve(__dirname, '.env')); - const envs = Object.keys(runTimeEnvs).filter((env) => !buildTimeEnvs.includes(env)); + const envs = Object.keys(envsMap).filter((env) => !buildTimeEnvs.includes(env)); const inconsistencies: Array = []; for (const env of envs) { - const hasPlaceholder = placeholders.includes(env); + const hasPlaceholder = runTimeEnvs.includes(env); if (!hasPlaceholder) { inconsistencies.push(env); } diff --git a/deploy/tools/envs-validator/tsconfig.json b/deploy/tools/envs-validator/tsconfig.json index 83b3cf53d3..a3ffc54e19 100644 --- a/deploy/tools/envs-validator/tsconfig.json +++ b/deploy/tools/envs-validator/tsconfig.json @@ -8,7 +8,7 @@ "nextjs-routes": ["./nextjs/nextjs-routes.d.ts"], } }, - "include": [ "../../../types/**/*.ts", "./index.ts", "./schema.ts" ], + "include": [ "../../../types/**/*.ts", "../../../global.d.ts", "./index.ts", "./schema.ts" ], "tsc-alias": { "verbose": true, "resolveFullPaths": true, diff --git a/deploy/tools/feature-reporter/tsconfig.json b/deploy/tools/feature-reporter/tsconfig.json index a75b90bc38..1fefb24f4c 100644 --- a/deploy/tools/feature-reporter/tsconfig.json +++ b/deploy/tools/feature-reporter/tsconfig.json @@ -8,7 +8,7 @@ "nextjs-routes": ["./nextjs/nextjs-routes.d.ts"], } }, - "include": [ "../../../configs/app/index.ts" ], + "include": [ "../../../configs/app/index.ts", "../../../global.d.ts" ], "tsc-alias": { "verbose": true, "resolveFullPaths": true, diff --git a/global.d.ts b/global.d.ts index 16d81d4ee1..2955f3f872 100644 --- a/global.d.ts +++ b/global.d.ts @@ -18,6 +18,7 @@ declare global { register: (...args: unknown) => void; }; abkw: string; + __envs: Record; } namespace NodeJS { diff --git a/jest/setup.ts b/jest/setup.ts index 8bb18552d3..c371af3c56 100644 --- a/jest/setup.ts +++ b/jest/setup.ts @@ -1,7 +1,11 @@ +import dotenv from 'dotenv'; + import fetchMock from 'jest-fetch-mock'; fetchMock.enableMocks(); +const envs = dotenv.config({ path: './configs/envs/.env.jest' }); + Object.defineProperty(window, 'matchMedia', { writable: true, value: jest.fn().mockImplementation(query => ({ @@ -16,6 +20,11 @@ Object.defineProperty(window, 'matchMedia', { })), }); +Object.defineProperty(window, '__envs', { + writable: true, + value: envs.parsed || {}, +}); + // eslint-disable-next-line no-console const consoleError = console.error; diff --git a/package.json b/package.json index c916bfc822..58ead28f36 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,9 @@ "lint:tsc": "tsc -p ./tsconfig.json", "prepare": "husky install", "format-svg": "svgo -r ./icons", - "test:pw": "./playwright/make-envs-script.sh && NODE_OPTIONS=\"--max-old-space-size=4096\" ./node_modules/.bin/dotenv -e ./configs/envs/.env.pw -- playwright test -c playwright-ct.config.ts", - "test:pw:local": "export NODE_PATH=$(pwd)/node_modules && rm -rf ./playwright/.cache && yarn test:pw", - "test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.35.1-focal ./playwright/run-tests.sh", + "test:pw": "./tools/scripts/pw.sh", + "test:pw:local": "export NODE_PATH=$(pwd)/node_modules && yarn test:pw", + "test:pw:docker": "docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.35.1-focal ./tools/scripts/pw.docker.sh", "test:pw:ci": "yarn test:pw --project=$PW_PROJECT", "test:jest": "jest", "test:jest:watch": "jest --watch", diff --git a/pages/_document.tsx b/pages/_document.tsx index 325d2d3e46..6d21b3532c 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -39,6 +39,9 @@ class MyDocument extends Document { rel="stylesheet" /> + { /* eslint-disable-next-line @next/next/no-sync-scripts */ } + + diff --git a/playwright/make-envs-script.sh b/playwright/make-envs-script.sh deleted file mode 100755 index f48f9ec314..0000000000 --- a/playwright/make-envs-script.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -targetFile='./playwright/envs.js' - -declare -a envFiles=('./configs/envs/.env.pw') - -touch $targetFile; -truncate -s 0 $targetFile; - -echo "Creating script file with envs" - -echo "window.process = { env: { } };" >> $targetFile; - -for envFile in "${envFiles[@]}" -do - # read each env file - while read line; do - # if it is a comment or an empty line, continue to next one - if [ "${line:0:1}" == "#" ] || [ "${line}" == "" ]; then - continue - fi - - # split by "=" sign to get variable name and value - configName="$(cut -d'=' -f1 <<<"$line")"; - configValue="$(cut -d'=' -f2- <<<"$line")"; - - # if there is a value, escape it and add line to target file - escapedConfigValue=$(echo $configValue | sed s/\'/\"/g); - echo "window.process.env.${configName} = localStorage.getItem('${configName}') ?? '${escapedConfigValue}';" >> $targetFile; - done < $envFile -done - -echo "Done" \ No newline at end of file diff --git a/tools/scripts/dev.preset.sh b/tools/scripts/dev.preset.sh index 93f1609e11..1110089f72 100755 --- a/tools/scripts/dev.preset.sh +++ b/tools/scripts/dev.preset.sh @@ -22,13 +22,13 @@ fi # download assets for the running instance dotenv \ -e $config_file \ - -- bash -c './deploy/scripts/download_assets.sh ./public/assets' \ + -- bash -c './deploy/scripts/download_assets.sh ./public/assets' -# run the app +# generate envs.js file and run the app dotenv \ -v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \ -v NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0) \ -e $config_file \ -e $secrets_file \ - -- bash -c 'next dev -- -p $NEXT_PUBLIC_APP_PORT' | + -- bash -c './deploy/scripts/make_envs_script.sh && next dev -- -p $NEXT_PUBLIC_APP_PORT' | pino-pretty \ No newline at end of file diff --git a/playwright/run-tests.sh b/tools/scripts/pw.docker.sh similarity index 63% rename from playwright/run-tests.sh rename to tools/scripts/pw.docker.sh index 57ef908da6..30179e8c4c 100755 --- a/playwright/run-tests.sh +++ b/tools/scripts/pw.docker.sh @@ -1,5 +1,7 @@ -#!/bin/sh +#!/bin/bash + yarn install --modules-folder node_modules_linux + export NODE_PATH=$(pwd)/node_modules_linux -rm -rf ./playwright/.cache -yarn test:pw "$@" \ No newline at end of file + +yarn test:pw "$@" diff --git a/tools/scripts/pw.sh b/tools/scripts/pw.sh new file mode 100755 index 0000000000..a8fa9dd069 --- /dev/null +++ b/tools/scripts/pw.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +config_file="./configs/envs/.env.pw" + +rm -rf ./playwright/.cache + +dotenv \ + -e $config_file \ + -- bash -c './deploy/scripts/make_envs_script.sh' + +dotenv \ + -v NODE_OPTIONS=\"--max-old-space-size=4096\" \ + -e $config_file \ + -- playwright test -c playwright-ct.config.ts "$@" diff --git a/ui/pages/SearchResults.pw.tsx b/ui/pages/SearchResults.pw.tsx index 092b592c77..bbf1c60598 100644 --- a/ui/pages/SearchResults.pw.tsx +++ b/ui/pages/SearchResults.pw.tsx @@ -1,7 +1,7 @@ import { test, expect } from '@playwright/experimental-ct-react'; import React from 'react'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import { apps as appsMock } from 'mocks/apps/apps'; import * as searchMock from 'mocks/search/index'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; @@ -181,7 +181,7 @@ test('search by tx hash +@mobile', async({ mount, page }) => { }); test.describe('with apps', () => { - const MARKETPLACE_CONFIG_URL = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || ''; + const MARKETPLACE_CONFIG_URL = buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || ''; const extendedTest = test.extend({ context: contextWithEnvs([ { name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL }, diff --git a/ui/snippets/footer/Footer.pw.tsx b/ui/snippets/footer/Footer.pw.tsx index bc1ef7110f..9c39bc2b0a 100644 --- a/ui/snippets/footer/Footer.pw.tsx +++ b/ui/snippets/footer/Footer.pw.tsx @@ -2,7 +2,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react'; import React from 'react'; import type { WindowProvider } from 'wagmi'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import { FOOTER_LINKS } from 'mocks/config/footerLinks'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import TestApp from 'playwright/TestApp'; @@ -11,7 +11,7 @@ import * as configs from 'playwright/utils/configs'; import Footer from './Footer'; -const FOOTER_LINKS_URL = getExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', 'https://localhost:3000/footer-links.json') || ''; +const FOOTER_LINKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', 'https://localhost:3000/footer-links.json') || ''; const BACKEND_VERSION_API_URL = buildApiUrl('config_backend_version'); const INDEXING_ALERT_API_URL = buildApiUrl('homepage_indexing_status'); diff --git a/ui/snippets/header/Burger.pw.tsx b/ui/snippets/header/Burger.pw.tsx index d2d5e5dbf4..c42541b3da 100644 --- a/ui/snippets/header/Burger.pw.tsx +++ b/ui/snippets/header/Burger.pw.tsx @@ -1,7 +1,7 @@ import { test as base, expect, devices } from '@playwright/experimental-ct-react'; import React from 'react'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import authFixture from 'playwright/fixtures/auth'; import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; @@ -9,7 +9,7 @@ import TestApp from 'playwright/TestApp'; import Burger from './Burger'; -const FEATURED_NETWORKS_URL = getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; +const FEATURED_NETWORKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; const LOGO_URL = 'https://localhost:3000/my-logo.png'; base.use({ viewport: devices['iPhone 13 Pro'].viewport }); diff --git a/ui/snippets/navigation/NavigationDesktop.pw.tsx b/ui/snippets/navigation/NavigationDesktop.pw.tsx index f9f5b56d0c..ff6ab583d5 100644 --- a/ui/snippets/navigation/NavigationDesktop.pw.tsx +++ b/ui/snippets/navigation/NavigationDesktop.pw.tsx @@ -3,7 +3,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react'; import type { Locator } from '@playwright/test'; import React from 'react'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import * as cookies from 'lib/cookies'; import authFixture from 'playwright/fixtures/auth'; import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; @@ -21,7 +21,7 @@ const hooksConfig = { }, }; -const FEATURED_NETWORKS_URL = getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; +const FEATURED_NETWORKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; const test = base.extend({ context: contextWithEnvs([ diff --git a/ui/snippets/networkMenu/NetworkLogo.pw.tsx b/ui/snippets/networkMenu/NetworkLogo.pw.tsx index c58df05303..b741bc894c 100644 --- a/ui/snippets/networkMenu/NetworkLogo.pw.tsx +++ b/ui/snippets/networkMenu/NetworkLogo.pw.tsx @@ -2,7 +2,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react'; import type { Locator } from '@playwright/test'; import React from 'react'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import TestApp from 'playwright/TestApp'; import * as configs from 'playwright/utils/configs'; @@ -44,8 +44,8 @@ base.describe('placeholder logo', () => { }); base.describe('custom logo', () => { - const LOGO_URL = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || ''; - const ICON_URL = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || ''; + const LOGO_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || ''; + const ICON_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || ''; const test = base.extend({ context: contextWithEnvs([ { name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL }, @@ -91,10 +91,10 @@ base.describe('custom logo', () => { }); base.describe('custom logo with dark option -@default +@dark-mode', () => { - const LOGO_URL = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || ''; - const LOGO_URL_DARK = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', 'https://localhost:3000/my-logo.png') || ''; - const ICON_URL = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || ''; - const ICON_URL_DARK = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', 'https://localhost:3000/my-icon.png') || ''; + const LOGO_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || ''; + const LOGO_URL_DARK = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', 'https://localhost:3000/my-logo.png') || ''; + const ICON_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || ''; + const ICON_URL_DARK = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', 'https://localhost:3000/my-icon.png') || ''; const test = base.extend({ context: contextWithEnvs([ { name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL }, diff --git a/ui/snippets/networkMenu/NetworkMenu.pw.tsx b/ui/snippets/networkMenu/NetworkMenu.pw.tsx index 4eeae264c4..42098588a3 100644 --- a/ui/snippets/networkMenu/NetworkMenu.pw.tsx +++ b/ui/snippets/networkMenu/NetworkMenu.pw.tsx @@ -1,14 +1,14 @@ import { test, expect } from '@playwright/experimental-ct-react'; import React from 'react'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import TestApp from 'playwright/TestApp'; import NetworkMenu from './NetworkMenu'; -const FEATURED_NETWORKS_URL = getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; +const FEATURED_NETWORKS_URL = buildExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', 'https://localhost:3000/featured-networks.json') || ''; const extendedTest = test.extend({ context: contextWithEnvs([ diff --git a/ui/snippets/searchBar/SearchBar.pw.tsx b/ui/snippets/searchBar/SearchBar.pw.tsx index 2aa2d67d7a..b0967ed396 100644 --- a/ui/snippets/searchBar/SearchBar.pw.tsx +++ b/ui/snippets/searchBar/SearchBar.pw.tsx @@ -2,7 +2,7 @@ import { LightMode } from '@chakra-ui/react'; import { test as base, expect } from '@playwright/experimental-ct-react'; import React from 'react'; -import { getExternalAssetFilePath } from 'configs/app/utils'; +import { buildExternalAssetFilePath } from 'configs/app/utils'; import * as textAdMock from 'mocks/ad/textAd'; import { apps as appsMock } from 'mocks/apps/apps'; import * as searchMock from 'mocks/search/index'; @@ -273,7 +273,7 @@ test('recent keywords suggest +@mobile', async({ mount, page }) => { }); base.describe('with apps', () => { - const MARKETPLACE_CONFIG_URL = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || ''; + const MARKETPLACE_CONFIG_URL = buildExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', 'https://marketplace-config.json') || ''; const test = base.extend({ context: contextWithEnvs([ { name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },