Commit dbd4452d authored by tom goriunov's avatar tom goriunov Committed by GitHub

workaround for ENV replacement script (#1212)

* base implementation of client script with envs values

* better ENVs setup for pw tests

* fix tests
parent 01c7603b
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
/.next/ /.next/
/out/ /out/
/public/assets/ /public/assets/
/public/envs.js
# production # production
/build /build
......
...@@ -47,9 +47,9 @@ WORKDIR /app ...@@ -47,9 +47,9 @@ WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
# Generate .env.production with ENVs placeholders and save build args into .env file # Generate .env.registry with ENVs list and save build args into .env file
COPY --chmod=+x ./deploy/scripts/make_envs_template.sh ./ COPY --chmod=+x ./deploy/scripts/collect_envs.sh ./
RUN ./make_envs_template.sh ./docs/ENVS.md RUN ./collect_envs.sh ./docs/ENVS.md
# Next.js collects completely anonymous telemetry data about general usage. # Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry # Learn more here: https://nextjs.org/telemetry
...@@ -98,8 +98,9 @@ COPY --from=builder /app/deploy/tools/feature-reporter/index.js ./feature-report ...@@ -98,8 +98,9 @@ COPY --from=builder /app/deploy/tools/feature-reporter/index.js ./feature-report
# Copy scripts # Copy scripts
## Entripoint ## Entripoint
COPY --chmod=+x ./deploy/scripts/entrypoint.sh . COPY --chmod=+x ./deploy/scripts/entrypoint.sh .
## ENV replacer ## ENV validator and client script maker
COPY --chmod=+x ./deploy/scripts/replace_envs.sh . COPY --chmod=+x ./deploy/scripts/validate_envs.sh .
COPY --chmod=+x ./deploy/scripts/make_envs_script.sh .
## Assets downloader ## Assets downloader
COPY --chmod=+x ./deploy/scripts/download_assets.sh . COPY --chmod=+x ./deploy/scripts/download_assets.sh .
## Favicon generator ## Favicon generator
...@@ -109,7 +110,7 @@ RUN ["chmod", "-R", "777", "./deploy/tools/favicon-generator"] ...@@ -109,7 +110,7 @@ RUN ["chmod", "-R", "777", "./deploy/tools/favicon-generator"]
RUN ["chmod", "-R", "777", "./public"] RUN ["chmod", "-R", "777", "./public"]
# Copy ENVs files # Copy ENVs files
COPY --from=builder /app/.env.production . COPY --from=builder /app/.env.registry .
COPY --from=builder /app/.env . COPY --from=builder /app/.env .
# Automatically leverage output traces to reduce image size # Automatically leverage output traces to reduce image size
......
...@@ -2,9 +2,9 @@ import stripTrailingSlash from 'lib/stripTrailingSlash'; ...@@ -2,9 +2,9 @@ import stripTrailingSlash from 'lib/stripTrailingSlash';
import { getEnvValue } from './utils'; import { getEnvValue } from './utils';
const apiHost = getEnvValue(process.env.NEXT_PUBLIC_API_HOST); const apiHost = getEnvValue('NEXT_PUBLIC_API_HOST');
const apiSchema = getEnvValue(process.env.NEXT_PUBLIC_API_PROTOCOL) || 'https'; const apiSchema = getEnvValue('NEXT_PUBLIC_API_PROTOCOL') || 'https';
const apiPort = getEnvValue(process.env.NEXT_PUBLIC_API_PORT); const apiPort = getEnvValue('NEXT_PUBLIC_API_PORT');
const apiEndpoint = [ const apiEndpoint = [
apiSchema || 'https', apiSchema || 'https',
'://', '://',
...@@ -12,7 +12,7 @@ const apiEndpoint = [ ...@@ -12,7 +12,7 @@ const apiEndpoint = [
apiPort && ':' + apiPort, apiPort && ':' + apiPort,
].filter(Boolean).join(''); ].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 = [ const socketEndpoint = [
socketSchema, socketSchema,
'://', '://',
...@@ -24,7 +24,7 @@ const api = Object.freeze({ ...@@ -24,7 +24,7 @@ const api = Object.freeze({
host: apiHost, host: apiHost,
endpoint: apiEndpoint, endpoint: apiEndpoint,
socket: socketEndpoint, socket: socketEndpoint,
basePath: stripTrailingSlash(getEnvValue(process.env.NEXT_PUBLIC_API_BASE_PATH) || ''), basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_API_BASE_PATH') || ''),
}); });
export default api; export default api;
import { getEnvValue } from './utils'; import { getEnvValue } from './utils';
const appPort = getEnvValue(process.env.NEXT_PUBLIC_APP_PORT); const appPort = getEnvValue('NEXT_PUBLIC_APP_PORT');
const appSchema = getEnvValue(process.env.NEXT_PUBLIC_APP_PROTOCOL); const appSchema = getEnvValue('NEXT_PUBLIC_APP_PROTOCOL');
const appHost = getEnvValue(process.env.NEXT_PUBLIC_APP_HOST); const appHost = getEnvValue('NEXT_PUBLIC_APP_HOST');
const baseUrl = [ const baseUrl = [
appSchema || 'https', appSchema || 'https',
'://', '://',
...@@ -17,7 +17,7 @@ const app = Object.freeze({ ...@@ -17,7 +17,7 @@ const app = Object.freeze({
host: appHost, host: appHost,
port: appPort, port: appPort,
baseUrl, baseUrl,
useProxy: getEnvValue(process.env.NEXT_PUBLIC_USE_NEXT_JS_PROXY) === 'true', useProxy: getEnvValue('NEXT_PUBLIC_USE_NEXT_JS_PROXY') === 'true',
}); });
export default app; export default app;
...@@ -3,20 +3,20 @@ import { getEnvValue } from './utils'; ...@@ -3,20 +3,20 @@ import { getEnvValue } from './utils';
const DEFAULT_CURRENCY_DECIMALS = 18; const DEFAULT_CURRENCY_DECIMALS = 18;
const chain = Object.freeze({ const chain = Object.freeze({
id: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_ID), id: getEnvValue('NEXT_PUBLIC_NETWORK_ID'),
name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_NAME), name: getEnvValue('NEXT_PUBLIC_NETWORK_NAME'),
shortName: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_SHORT_NAME), shortName: getEnvValue('NEXT_PUBLIC_NETWORK_SHORT_NAME'),
currency: { currency: {
name: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_NAME), name: getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_NAME'),
symbol: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL), symbol: getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL'),
decimals: Number(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS)) || DEFAULT_CURRENCY_DECIMALS, decimals: Number(getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS')) || DEFAULT_CURRENCY_DECIMALS,
}, },
governanceToken: { 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), rpcUrl: getEnvValue('NEXT_PUBLIC_NETWORK_RPC_URL'),
isTestnet: getEnvValue(process.env.NEXT_PUBLIC_IS_TESTNET) === 'true', isTestnet: getEnvValue('NEXT_PUBLIC_IS_TESTNET') === 'true',
verificationType: getEnvValue(process.env.NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE) || 'mining', verificationType: getEnvValue('NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE') || 'mining',
}); });
export default chain; export default chain;
...@@ -5,12 +5,12 @@ import stripTrailingSlash from 'lib/stripTrailingSlash'; ...@@ -5,12 +5,12 @@ import stripTrailingSlash from 'lib/stripTrailingSlash';
import app from '../app'; import app from '../app';
import { getEnvValue } from '../utils'; 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 = (() => { const logoutUrl = (() => {
try { try {
const envUrl = getEnvValue(process.env.NEXT_PUBLIC_LOGOUT_URL); const envUrl = getEnvValue('NEXT_PUBLIC_LOGOUT_URL');
const auth0ClientId = getEnvValue(process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID); const auth0ClientId = getEnvValue('NEXT_PUBLIC_AUTH0_CLIENT_ID');
const returnUrl = authUrl + '/auth/logout'; const returnUrl = authUrl + '/auth/logout';
if (!envUrl || !auth0ClientId) { if (!envUrl || !auth0ClientId) {
...@@ -31,7 +31,7 @@ const title = 'My account'; ...@@ -31,7 +31,7 @@ const title = 'My account';
const config: Feature<{ authUrl: string; logoutUrl: string }> = (() => { const config: Feature<{ authUrl: string; logoutUrl: string }> = (() => {
if ( if (
getEnvValue(process.env.NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED) === 'true' && getEnvValue('NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED') === 'true' &&
authUrl && authUrl &&
logoutUrl logoutUrl
) { ) {
......
...@@ -4,7 +4,7 @@ import { getEnvValue } from '../utils'; ...@@ -4,7 +4,7 @@ import { getEnvValue } from '../utils';
import account from './account'; import account from './account';
import verifiedTokens from './verifiedTokens'; 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"'; const title = 'Address verification in "My account"';
......
...@@ -6,7 +6,7 @@ import type { AdBannerProviders } from 'types/client/adProviders'; ...@@ -6,7 +6,7 @@ import type { AdBannerProviders } from 'types/client/adProviders';
import { getEnvValue, parseEnvJson } from '../utils'; import { getEnvValue, parseEnvJson } from '../utils';
const provider: AdBannerProviders = (() => { 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'; return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'slise';
})(); })();
...@@ -27,8 +27,8 @@ type AdsBannerFeaturePayload = { ...@@ -27,8 +27,8 @@ type AdsBannerFeaturePayload = {
const config: Feature<AdsBannerFeaturePayload> = (() => { const config: Feature<AdsBannerFeaturePayload> = (() => {
if (provider === 'adbutler') { if (provider === 'adbutler') {
const desktopConfig = parseEnvJson<AdButlerConfig>(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP)); const desktopConfig = parseEnvJson<AdButlerConfig>(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP'));
const mobileConfig = parseEnvJson<AdButlerConfig>(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE)); const mobileConfig = parseEnvJson<AdButlerConfig>(getEnvValue('NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE'));
if (desktopConfig && mobileConfig) { if (desktopConfig && mobileConfig) {
return Object.freeze({ return Object.freeze({
......
...@@ -5,7 +5,7 @@ import type { AdTextProviders } from 'types/client/adProviders'; ...@@ -5,7 +5,7 @@ import type { AdTextProviders } from 'types/client/adProviders';
import { getEnvValue } from '../utils'; import { getEnvValue } from '../utils';
const provider: AdTextProviders = (() => { 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'; return envValue && SUPPORTED_AD_BANNER_PROVIDERS.includes(envValue) ? envValue : 'coinzilla';
})(); })();
......
...@@ -5,14 +5,14 @@ import { getEnvValue } from '../utils'; ...@@ -5,14 +5,14 @@ import { getEnvValue } from '../utils';
const title = 'Beacon chain'; const title = 'Beacon chain';
const config: Feature<{ currency: { symbol: string } }> = (() => { 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({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
currency: { currency: {
symbol: symbol:
getEnvValue(process.env.NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL) || getEnvValue('NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL') ||
getEnvValue(process.env.NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL) || getEnvValue('NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL') ||
'', // maybe we need some other default value here '', // maybe we need some other default value here
}, },
}); });
......
...@@ -3,7 +3,7 @@ import type { Feature } from './types'; ...@@ -3,7 +3,7 @@ import type { Feature } from './types';
import chain from '../chain'; import chain from '../chain';
import { getEnvValue } from '../utils'; 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.)'; const title = 'Blockchain interaction (writing to contract, etc.)';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'Google analytics';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'GraphQL API documentation';
......
...@@ -4,8 +4,8 @@ import chain from '../chain'; ...@@ -4,8 +4,8 @@ import chain from '../chain';
import { getEnvValue, getExternalAssetFilePath } from '../utils'; import { getEnvValue, getExternalAssetFilePath } from '../utils';
// config file will be downloaded at run-time and saved in the public folder // 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 configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL');
const submitFormUrl = getEnvValue(process.env.NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM); const submitFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM');
const title = 'Marketplace'; const title = 'Marketplace';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'Mixpanel analytics';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'REST API documentation';
......
...@@ -5,11 +5,11 @@ import { getEnvValue } from '../utils'; ...@@ -5,11 +5,11 @@ import { getEnvValue } from '../utils';
const title = 'Rollup (L2) chain'; const title = 'Rollup (L2) chain';
const config: Feature<{ L1BaseUrl: string; withdrawalUrl: string }> = (() => { const config: Feature<{ L1BaseUrl: string; withdrawalUrl: string }> = (() => {
const L1BaseUrl = getEnvValue(process.env.NEXT_PUBLIC_L1_BASE_URL); const L1BaseUrl = getEnvValue('NEXT_PUBLIC_L1_BASE_URL');
const withdrawalUrl = getEnvValue(process.env.NEXT_PUBLIC_L2_WITHDRAWAL_URL); const withdrawalUrl = getEnvValue('NEXT_PUBLIC_L2_WITHDRAWAL_URL');
if ( if (
getEnvValue(process.env.NEXT_PUBLIC_IS_L2_NETWORK) === 'true' && getEnvValue('NEXT_PUBLIC_IS_L2_NETWORK') === 'true' &&
L1BaseUrl && L1BaseUrl &&
withdrawalUrl withdrawalUrl
) { ) {
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'Sentry error monitoring';
...@@ -12,9 +12,9 @@ const config: Feature<{ dsn: string; environment: string | undefined; cspReportU ...@@ -12,9 +12,9 @@ const config: Feature<{ dsn: string; environment: string | undefined; cspReportU
title, title,
isEnabled: true, isEnabled: true,
dsn, dsn,
environment: getEnvValue(process.env.NEXT_PUBLIC_APP_ENV) || getEnvValue(process.env.NODE_ENV), environment: getEnvValue('NEXT_PUBLIC_APP_ENV') || getEnvValue('NODE_ENV'),
cspReportUrl: getEnvValue(process.env.SENTRY_CSP_REPORT_URI), cspReportUrl: getEnvValue('SENTRY_CSP_REPORT_URI'),
instance: getEnvValue(process.env.NEXT_PUBLIC_APP_INSTANCE), instance: getEnvValue('NEXT_PUBLIC_APP_INSTANCE'),
}); });
} }
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'Solidity to UML diagrams';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'Blockchain statistics';
......
...@@ -2,7 +2,7 @@ import type { Feature } from './types'; ...@@ -2,7 +2,7 @@ import type { Feature } from './types';
import { getEnvValue } from '../utils'; 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'; const title = 'Verified tokens info';
......
...@@ -5,7 +5,7 @@ import type { WalletType } from 'types/client/wallets'; ...@@ -5,7 +5,7 @@ import type { WalletType } from 'types/client/wallets';
import { getEnvValue, parseEnvJson } from '../utils'; import { getEnvValue, parseEnvJson } from '../utils';
const wallets = ((): Array<WalletType> | undefined => { const wallets = ((): Array<WalletType> | undefined => {
const envValue = getEnvValue(process.env.NEXT_PUBLIC_WEB3_WALLETS); const envValue = getEnvValue('NEXT_PUBLIC_WEB3_WALLETS');
if (envValue === 'none') { if (envValue === 'none') {
return; return;
} }
...@@ -28,7 +28,7 @@ const config: Feature<{ wallets: Array<WalletType>; addToken: { isDisabled: bool ...@@ -28,7 +28,7 @@ const config: Feature<{ wallets: Array<WalletType>; addToken: { isDisabled: bool
isEnabled: true, isEnabled: true,
wallets, wallets,
addToken: { 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: {}, addNetwork: {},
}); });
......
...@@ -4,10 +4,10 @@ import { getEnvValue, getExternalAssetFilePath } from './utils'; ...@@ -4,10 +4,10 @@ import { getEnvValue, getExternalAssetFilePath } from './utils';
const defaultImageUrl = app.baseUrl + '/static/og_placeholder.png'; const defaultImageUrl = app.baseUrl + '/static/og_placeholder.png';
const meta = Object.freeze({ 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: { og: {
description: getEnvValue(process.env.NEXT_PUBLIC_OG_DESCRIPTION) || '', description: getEnvValue('NEXT_PUBLIC_OG_DESCRIPTION') || '',
imageUrl: getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL', process.env.NEXT_PUBLIC_OG_IMAGE_URL) || defaultImageUrl, imageUrl: getExternalAssetFilePath('NEXT_PUBLIC_OG_IMAGE_URL') || defaultImageUrl,
}, },
}); });
......
...@@ -2,6 +2,6 @@ import { getEnvValue } from './utils'; ...@@ -2,6 +2,6 @@ import { getEnvValue } from './utils';
export default Object.freeze({ export default Object.freeze({
reCaptcha: { reCaptcha: {
siteKey: getEnvValue(process.env.NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY), siteKey: getEnvValue('NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY'),
}, },
}); });
...@@ -11,36 +11,36 @@ const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% ...@@ -11,36 +11,36 @@ const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0%
const UI = Object.freeze({ const UI = Object.freeze({
sidebar: { sidebar: {
logo: { logo: {
'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', process.env.NEXT_PUBLIC_NETWORK_LOGO), 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO'),
dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK', process.env.NEXT_PUBLIC_NETWORK_LOGO_DARK), dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO_DARK'),
}, },
icon: { icon: {
'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', process.env.NEXT_PUBLIC_NETWORK_ICON), 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON'),
dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', process.env.NEXT_PUBLIC_NETWORK_ICON_DARK), dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK'),
}, },
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue(process.env.NEXT_PUBLIC_OTHER_LINKS)) || [], otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue('NEXT_PUBLIC_OTHER_LINKS')) || [],
featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS', process.env.NEXT_PUBLIC_FEATURED_NETWORKS), featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS'),
}, },
footer: { footer: {
links: getExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS', process.env.NEXT_PUBLIC_FOOTER_LINKS), links: getExternalAssetFilePath('NEXT_PUBLIC_FOOTER_LINKS'),
frontendVersion: getEnvValue(process.env.NEXT_PUBLIC_GIT_TAG), frontendVersion: getEnvValue('NEXT_PUBLIC_GIT_TAG'),
frontendCommit: getEnvValue(process.env.NEXT_PUBLIC_GIT_COMMIT_SHA), frontendCommit: getEnvValue('NEXT_PUBLIC_GIT_COMMIT_SHA'),
}, },
homepage: { homepage: {
charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_CHARTS)) || [], charts: parseEnvJson<Array<ChainIndicatorId>>(getEnvValue('NEXT_PUBLIC_HOMEPAGE_CHARTS')) || [],
plate: { plate: {
background: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND) || HOMEPAGE_PLATE_BACKGROUND_DEFAULT, background: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND') || HOMEPAGE_PLATE_BACKGROUND_DEFAULT,
textColor: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR) || 'white', textColor: getEnvValue('NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR') || 'white',
}, },
showGasTracker: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER) === 'false' ? false : true, showGasTracker: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER') === 'false' ? false : true,
showAvgBlockTime: getEnvValue(process.env.NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME) === 'false' ? false : true, showAvgBlockTime: getEnvValue('NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME') === 'false' ? false : true,
}, },
views, views,
indexingAlert: { indexingAlert: {
isHidden: getEnvValue(process.env.NEXT_PUBLIC_HIDE_INDEXING_ALERT), isHidden: getEnvValue('NEXT_PUBLIC_HIDE_INDEXING_ALERT'),
}, },
explorers: { explorers: {
items: parseEnvJson<Array<NetworkExplorer>>(getEnvValue(process.env.NEXT_PUBLIC_NETWORK_EXPLORERS)) || [], items: parseEnvJson<Array<NetworkExplorer>>(getEnvValue('NEXT_PUBLIC_NETWORK_EXPLORERS')) || [],
}, },
}); });
......
...@@ -4,7 +4,7 @@ import { IDENTICON_TYPES } from 'types/views/address'; ...@@ -4,7 +4,7 @@ import { IDENTICON_TYPES } from 'types/views/address';
import { getEnvValue } from 'configs/app/utils'; import { getEnvValue } from 'configs/app/utils';
const identiconType: IdenticonType = (() => { 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'; return IDENTICON_TYPES.find((type) => value === type) || 'jazzicon';
})(); })();
......
...@@ -4,7 +4,7 @@ import { BLOCK_FIELDS_IDS } from 'types/views/block'; ...@@ -4,7 +4,7 @@ import { BLOCK_FIELDS_IDS } from 'types/views/block';
import { getEnvValue, parseEnvJson } from 'configs/app/utils'; import { getEnvValue, parseEnvJson } from 'configs/app/utils';
const blockHiddenFields = (() => { const blockHiddenFields = (() => {
const parsedValue = parseEnvJson<Array<BlockFieldId>>(getEnvValue(process.env.NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS)) || []; const parsedValue = parseEnvJson<Array<BlockFieldId>>(getEnvValue('NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS')) || [];
if (!Array.isArray(parsedValue)) { if (!Array.isArray(parsedValue)) {
return undefined; return undefined;
......
import isBrowser from 'lib/isBrowser';
import * as regexp from 'lib/regexp'; import * as regexp from 'lib/regexp';
export const getEnvValue = <T extends string>(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 = <DataType>(env: string | undefined): DataType | null => { export const parseEnvJson = <DataType>(env: string | undefined): DataType | null => {
try { try {
...@@ -10,15 +23,19 @@ export const parseEnvJson = <DataType>(env: string | undefined): DataType | null ...@@ -10,15 +23,19 @@ export const parseEnvJson = <DataType>(env: string | undefined): DataType | null
} }
}; };
export const getExternalAssetFilePath = (envName: string, envValue: string | undefined) => { export const getExternalAssetFilePath = (envName: string) => {
const parsedValue = getEnvValue(envValue); const parsedValue = getEnvValue(envName);
if (!parsedValue) { if (!parsedValue) {
return; return;
} }
const fileName = envName.replace(/^NEXT_PUBLIC_/, '').replace(/_URL$/, '').toLowerCase(); return buildExternalAssetFilePath(envName, parsedValue);
const fileExtension = parsedValue.match(regexp.FILE_EXTENSION)?.[1]; };
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 }`; return `/assets/${ fileName }.${ fileExtension }`;
}; };
# Set of ENVs for Playwright components tests # 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 # app configuration
NEXT_PUBLIC_APP_PROTOCOL=http NEXT_PUBLIC_APP_PROTOCOL=http
...@@ -29,18 +26,10 @@ NEXT_PUBLIC_API_BASE_PATH=/ ...@@ -29,18 +26,10 @@ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs','coin_price','market_cap']
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=true
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=true
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=
## sidebar ## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=
NEXT_PUBLIC_NETWORK_LOGO=
NEXT_PUBLIC_NETWORK_LOGO_DARK=
NEXT_PUBLIC_NETWORK_ICON=
NEXT_PUBLIC_NETWORK_ICON_DARK=
## footer ## footer
NEXT_PUBLIC_GIT_TAG=v1.0.11 NEXT_PUBLIC_GIT_TAG=v1.0.11
NEXT_PUBLIC_FOOTER_LINKS=
## views ## views
NEXT_PUBLIC_VIEWS_BLOCK_HIDDEN_FIELDS=
## misc ## 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'}}] 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 ...@@ -50,8 +39,6 @@ NEXT_PUBLIC_APP_INSTANCE=pw
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://localhost:3000/marketplace-config.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://localhost:3000/marketplace-submit-form
NEXT_PUBLIC_IS_L2_NETWORK=false NEXT_PUBLIC_IS_L2_NETWORK=false
NEXT_PUBLIC_L1_BASE_URL=
NEXT_PUBLIC_L2_WITHDRAWAL_URL=
NEXT_PUBLIC_AD_BANNER_PROVIDER=slise NEXT_PUBLIC_AD_BANNER_PROVIDER=slise
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_AUTH_URL=http://localhost:3100 NEXT_PUBLIC_AUTH_URL=http://localhost:3100
...@@ -61,4 +48,3 @@ NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004 ...@@ -61,4 +48,3 @@ NEXT_PUBLIC_STATS_API_HOST=https://localhost:3004
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005 NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://localhost:3006
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_HAS_BEACON_CHAIN=
...@@ -9,11 +9,11 @@ fi ...@@ -9,11 +9,11 @@ fi
input_file="$1" input_file="$1"
prefix="NEXT_PUBLIC_" 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 # 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 # This variables will have dummy values assigned to them
make_envs_template_file() { make_registry_file() {
output_file=".env.production" output_file=".env.registry"
# Check if file already exists and empty its content if it does # Check if file already exists and empty its content if it does
if [ -f "$output_file" ]; then if [ -f "$output_file" ]; then
...@@ -21,7 +21,7 @@ make_envs_template_file() { ...@@ -21,7 +21,7 @@ make_envs_template_file() {
fi fi
grep -oE "${prefix}[[:alnum:]_]+" "$input_file" | sort -u | while IFS= read -r var_name; do 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 done
} }
...@@ -41,5 +41,5 @@ save_build-time_envs() { ...@@ -41,5 +41,5 @@ save_build-time_envs() {
done done
} }
make_envs_template_file make_registry_file
save_build-time_envs save_build-time_envs
\ No newline at end of file
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
# Download external assets # Download external assets
./download_assets.sh ./public/assets ./download_assets.sh ./public/assets
# Check run-time ENVs values integrity # Check run-time ENVs values
node "$(dirname "$0")/envs-validator.js" "$input" ./validate_envs.sh
if [ $? != 0 ]; then if [ $? -ne 0 ]; then
exit 1 exit 1
fi fi
...@@ -18,8 +18,8 @@ else ...@@ -18,8 +18,8 @@ else
fi fi
echo echo
# Execute script for replace build-time ENVs placeholders with their values at runtime # Create envs.js file with run-time environment variables for the client app
./replace_envs.sh ./make_envs_script.sh
# Print list of enabled features # Print list of enabled features
node ./feature-reporter.js node ./feature-reporter.js
......
#!/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."
#!/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
#!/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
/node_modules /node_modules
/public /public
.env .env
.env.production .env.registry
index.js index.js
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
export NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) export NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD)
export NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0) 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 yarn build
......
...@@ -81,17 +81,17 @@ async function getExternalJsonContent(fileName: string, envValue: string): Promi ...@@ -81,17 +81,17 @@ async function getExternalJsonContent(fileName: string, envValue: string): Promi
}); });
} }
async function checkPlaceholdersCongruity(runTimeEnvs: Record<string, string>) { async function checkPlaceholdersCongruity(envsMap: Record<string, string>) {
try { try {
console.log(`🌀 Checking environment variables and their placeholders congruity...`); 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 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<string> = []; const inconsistencies: Array<string> = [];
for (const env of envs) { for (const env of envs) {
const hasPlaceholder = placeholders.includes(env); const hasPlaceholder = runTimeEnvs.includes(env);
if (!hasPlaceholder) { if (!hasPlaceholder) {
inconsistencies.push(env); inconsistencies.push(env);
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"nextjs-routes": ["./nextjs/nextjs-routes.d.ts"], "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": { "tsc-alias": {
"verbose": true, "verbose": true,
"resolveFullPaths": true, "resolveFullPaths": true,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"nextjs-routes": ["./nextjs/nextjs-routes.d.ts"], "nextjs-routes": ["./nextjs/nextjs-routes.d.ts"],
} }
}, },
"include": [ "../../../configs/app/index.ts" ], "include": [ "../../../configs/app/index.ts", "../../../global.d.ts" ],
"tsc-alias": { "tsc-alias": {
"verbose": true, "verbose": true,
"resolveFullPaths": true, "resolveFullPaths": true,
......
...@@ -18,6 +18,7 @@ declare global { ...@@ -18,6 +18,7 @@ declare global {
register: (...args: unknown) => void; register: (...args: unknown) => void;
}; };
abkw: string; abkw: string;
__envs: Record<string, string>;
} }
namespace NodeJS { namespace NodeJS {
......
import dotenv from 'dotenv';
import fetchMock from 'jest-fetch-mock'; import fetchMock from 'jest-fetch-mock';
fetchMock.enableMocks(); fetchMock.enableMocks();
const envs = dotenv.config({ path: './configs/envs/.env.jest' });
Object.defineProperty(window, 'matchMedia', { Object.defineProperty(window, 'matchMedia', {
writable: true, writable: true,
value: jest.fn().mockImplementation(query => ({ value: jest.fn().mockImplementation(query => ({
...@@ -16,6 +20,11 @@ Object.defineProperty(window, 'matchMedia', { ...@@ -16,6 +20,11 @@ Object.defineProperty(window, 'matchMedia', {
})), })),
}); });
Object.defineProperty(window, '__envs', {
writable: true,
value: envs.parsed || {},
});
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
const consoleError = console.error; const consoleError = console.error;
......
...@@ -20,9 +20,9 @@ ...@@ -20,9 +20,9 @@
"lint:tsc": "tsc -p ./tsconfig.json", "lint:tsc": "tsc -p ./tsconfig.json",
"prepare": "husky install", "prepare": "husky install",
"format-svg": "svgo -r ./icons", "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": "./tools/scripts/pw.sh",
"test:pw:local": "export NODE_PATH=$(pwd)/node_modules && rm -rf ./playwright/.cache && yarn test:pw", "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 ./playwright/run-tests.sh", "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:pw:ci": "yarn test:pw --project=$PW_PROJECT",
"test:jest": "jest", "test:jest": "jest",
"test:jest:watch": "jest --watch", "test:jest:watch": "jest --watch",
......
...@@ -39,6 +39,9 @@ class MyDocument extends Document { ...@@ -39,6 +39,9 @@ class MyDocument extends Document {
rel="stylesheet" rel="stylesheet"
/> />
{ /* eslint-disable-next-line @next/next/no-sync-scripts */ }
<script src="/envs.js"/>
{ /* FAVICON */ } { /* FAVICON */ }
<link rel="icon" href="/favicon/favicon.ico" sizes="48x48"/> <link rel="icon" href="/favicon/favicon.ico" sizes="48x48"/>
<link rel="icon" sizes="32x32" type="image/png" href="/favicon/favicon-32x32.png"/> <link rel="icon" sizes="32x32" type="image/png" href="/favicon/favicon-32x32.png"/>
......
...@@ -68,7 +68,7 @@ const config: PlaywrightTestConfig = defineConfig({ ...@@ -68,7 +68,7 @@ const config: PlaywrightTestConfig = defineConfig({
}, },
}, },
define: { define: {
'process.env': 'process.env', // Port over window.process envs 'process.env': '__envs', // Port process.env over window.__envs
}, },
}, },
}, },
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/playwright/envs.js"></script> <script type="module" src="/public/envs.js"></script>
<script type="module" src="/playwright/index.ts"></script> <script type="module" src="/playwright/index.ts"></script>
</body> </body>
</html> </html>
#!/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
...@@ -22,13 +22,13 @@ fi ...@@ -22,13 +22,13 @@ fi
# download assets for the running instance # download assets for the running instance
dotenv \ dotenv \
-e $config_file \ -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 \ dotenv \
-v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \ -v NEXT_PUBLIC_GIT_COMMIT_SHA=$(git rev-parse --short HEAD) \
-v NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0) \ -v NEXT_PUBLIC_GIT_TAG=$(git describe --tags --abbrev=0) \
-e $config_file \ -e $config_file \
-e $secrets_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 pino-pretty
\ No newline at end of file
#!/bin/sh #!/bin/bash
yarn install --modules-folder node_modules_linux yarn install --modules-folder node_modules_linux
export NODE_PATH=$(pwd)/node_modules_linux export NODE_PATH=$(pwd)/node_modules_linux
rm -rf ./playwright/.cache
yarn test:pw "$@" yarn test:pw "$@"
#!/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 "$@"
import { test, expect } from '@playwright/experimental-ct-react'; import { test, expect } from '@playwright/experimental-ct-react';
import React from '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 { apps as appsMock } from 'mocks/apps/apps';
import * as searchMock from 'mocks/search/index'; import * as searchMock from 'mocks/search/index';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
...@@ -181,7 +181,7 @@ test('search by tx hash +@mobile', async({ mount, page }) => { ...@@ -181,7 +181,7 @@ test('search by tx hash +@mobile', async({ mount, page }) => {
}); });
test.describe('with apps', () => { 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({ const extendedTest = test.extend({
context: contextWithEnvs([ context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL }, { name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
......
...@@ -2,7 +2,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react'; ...@@ -2,7 +2,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react';
import React from 'react'; import React from 'react';
import type { WindowProvider } from 'wagmi'; 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 { FOOTER_LINKS } from 'mocks/config/footerLinks';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
...@@ -11,7 +11,7 @@ import * as configs from 'playwright/utils/configs'; ...@@ -11,7 +11,7 @@ import * as configs from 'playwright/utils/configs';
import Footer from './Footer'; 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 BACKEND_VERSION_API_URL = buildApiUrl('config_backend_version');
const INDEXING_ALERT_API_URL = buildApiUrl('homepage_indexing_status'); const INDEXING_ALERT_API_URL = buildApiUrl('homepage_indexing_status');
......
import { test as base, expect, devices } from '@playwright/experimental-ct-react'; import { test as base, expect, devices } from '@playwright/experimental-ct-react';
import React from '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 { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import authFixture from 'playwright/fixtures/auth'; import authFixture from 'playwright/fixtures/auth';
import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs';
...@@ -9,7 +9,7 @@ import TestApp from 'playwright/TestApp'; ...@@ -9,7 +9,7 @@ import TestApp from 'playwright/TestApp';
import Burger from './Burger'; 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'; const LOGO_URL = 'https://localhost:3000/my-logo.png';
base.use({ viewport: devices['iPhone 13 Pro'].viewport }); base.use({ viewport: devices['iPhone 13 Pro'].viewport });
......
...@@ -3,7 +3,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react'; ...@@ -3,7 +3,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test'; import type { Locator } from '@playwright/test';
import React from 'react'; import React from 'react';
import { getExternalAssetFilePath } from 'configs/app/utils'; import { buildExternalAssetFilePath } from 'configs/app/utils';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import authFixture from 'playwright/fixtures/auth'; import authFixture from 'playwright/fixtures/auth';
import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs';
...@@ -21,7 +21,7 @@ const hooksConfig = { ...@@ -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({ const test = base.extend({
context: contextWithEnvs([ context: contextWithEnvs([
......
...@@ -2,7 +2,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react'; ...@@ -2,7 +2,7 @@ import { test as base, expect } from '@playwright/experimental-ct-react';
import type { Locator } from '@playwright/test'; import type { Locator } from '@playwright/test';
import React from 'react'; import React from 'react';
import { getExternalAssetFilePath } from 'configs/app/utils'; import { buildExternalAssetFilePath } from 'configs/app/utils';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import * as configs from 'playwright/utils/configs'; import * as configs from 'playwright/utils/configs';
...@@ -44,8 +44,8 @@ base.describe('placeholder logo', () => { ...@@ -44,8 +44,8 @@ base.describe('placeholder logo', () => {
}); });
base.describe('custom logo', () => { base.describe('custom logo', () => {
const LOGO_URL = getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_LOGO', 'https://localhost:3000/my-logo.png') || ''; const LOGO_URL = buildExternalAssetFilePath('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 ICON_URL = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON', 'https://localhost:3000/my-icon.png') || '';
const test = base.extend({ const test = base.extend({
context: contextWithEnvs([ context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL }, { name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
...@@ -91,10 +91,10 @@ base.describe('custom logo', () => { ...@@ -91,10 +91,10 @@ base.describe('custom logo', () => {
}); });
base.describe('custom logo with dark option -@default +@dark-mode', () => { 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 = buildExternalAssetFilePath('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 LOGO_URL_DARK = buildExternalAssetFilePath('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 = buildExternalAssetFilePath('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 ICON_URL_DARK = buildExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK', 'https://localhost:3000/my-icon.png') || '';
const test = base.extend({ const test = base.extend({
context: contextWithEnvs([ context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL }, { name: 'NEXT_PUBLIC_NETWORK_LOGO', value: LOGO_URL },
......
import { test, expect } from '@playwright/experimental-ct-react'; import { test, expect } from '@playwright/experimental-ct-react';
import React from '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 { FEATURED_NETWORKS_MOCK } from 'mocks/config/network';
import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; import contextWithEnvs from 'playwright/fixtures/contextWithEnvs';
import TestApp from 'playwright/TestApp'; import TestApp from 'playwright/TestApp';
import NetworkMenu from './NetworkMenu'; 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({ const extendedTest = test.extend({
context: contextWithEnvs([ context: contextWithEnvs([
......
...@@ -2,7 +2,7 @@ import { LightMode } from '@chakra-ui/react'; ...@@ -2,7 +2,7 @@ import { LightMode } from '@chakra-ui/react';
import { test as base, expect } from '@playwright/experimental-ct-react'; import { test as base, expect } from '@playwright/experimental-ct-react';
import React from '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 * as textAdMock from 'mocks/ad/textAd';
import { apps as appsMock } from 'mocks/apps/apps'; import { apps as appsMock } from 'mocks/apps/apps';
import * as searchMock from 'mocks/search/index'; import * as searchMock from 'mocks/search/index';
...@@ -273,7 +273,7 @@ test('recent keywords suggest +@mobile', async({ mount, page }) => { ...@@ -273,7 +273,7 @@ test('recent keywords suggest +@mobile', async({ mount, page }) => {
}); });
base.describe('with apps', () => { 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({ const test = base.extend({
context: contextWithEnvs([ context: contextWithEnvs([
{ name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL }, { name: 'NEXT_PUBLIC_MARKETPLACE_CONFIG_URL', value: MARKETPLACE_CONFIG_URL },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment