1
0
Fork 0

feat: add yaml feature management (#18125)

* feat: add yaml feature management

Add yaml feature file per build type.
Also add method to parse yaml and set
enabled features env to true. The build
process will then replace any process.env[feature]
that exists on the config by its value

* chore: add example for desktop

* Added initial draft of build features

* [TMP] Sync between computers

* Is able to succesfully build stable extension with snaps feature

* Removing var context from builds.yml

* Add asssets to builds.yml

* Minor bug fixes and removing debug logs

* [WIP] Test changes

* Removed TODOs

* Fix regession bug

Also
* remove debug logs
* merge Variables.set and Variables.setMany with an overload

* Fix build, lint and a bunch of issues

* Update LavaMoat policies

* Re-add desktop build type

* Fix some tests

* Fix desktop build

* Define some env variables used by MV3

* Fix lint

* Fix remove-fenced-code tests

* Fix README typo

* Move new code

* Fix missing asset copy

* Move Jest env setup

* Fix path for test after rebase

* Fix code fences

* Fix fencing and LavaMoat policies

* Fix MMI code-fencing after rebase

* Fix MMI code fencing after merge

* Fix more MMI code fencing

---------

Co-authored-by: cryptotavares <joao.tavares@consensys.net>
Co-authored-by: Frederik Bolding <frederik.bolding@gmail.com>
Co-authored-by: Brad Decker <bhdecker84@gmail.com>
This commit is contained in:
Olaf Tomalka 2023-04-25 21:32:51 +07:00 committed by GitHub
parent 4df363b251
commit 95c37e1ba3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
183 changed files with 1565 additions and 933 deletions

View File

@ -1,11 +1,15 @@
; Extra environment variables
PASSWORD=METAMASK PASSWORD
; Defaults are set in builds.yml
; This variable is required
INFURA_PROJECT_ID=00000000000
SEGMENT_WRITE_KEY=
SWAPS_USE_DEV_APIS=
PORTFOLIO_URL=
TRANSACTION_SECURITY_PROVIDER=
MULTICHAIN=
;PASSWORD=METAMASK PASSWORD
,SEGMENT_WRITE_KEY=
;SWAPS_USE_DEV_APIS=
;PORTFOLIO_URL=
;TRANSACTION_SECURITY_PROVIDER=
;MULTICHAIN=
; Set this to test changes to the phishing warning page.
PHISHING_WARNING_PAGE_URL=
;PHISHING_WARNING_PAGE_URL=

View File

@ -18,7 +18,7 @@ import {
ENVIRONMENT_TYPE_FULLSCREEN,
EXTENSION_MESSAGES,
PLATFORM_FIREFOX,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
MESSAGE_TYPE,
///: END:ONLY_INCLUDE_IN
} from '../../shared/constants/app';
@ -55,7 +55,7 @@ import { deferredPromise, getPlatform } from './lib/util';
/* eslint-enable import/first */
/* eslint-disable import/order */
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
import {
CONNECTION_TYPE_EXTERNAL,
CONNECTION_TYPE_INTERNAL,
@ -105,7 +105,7 @@ const PHISHING_WARNING_PAGE_TIMEOUT = ONE_SECOND_IN_MILLISECONDS;
const ACK_KEEP_ALIVE_MESSAGE = 'ACK_KEEP_ALIVE_MESSAGE';
const WORKER_KEEP_ALIVE_MESSAGE = 'WORKER_KEEP_ALIVE_MESSAGE';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
const OVERRIDE_ORIGIN = {
EXTENSION: 'EXTENSION',
DESKTOP: 'DESKTOP_APP',
@ -263,7 +263,7 @@ async function initialize() {
const initState = await loadStateFromPersistence();
const initLangCode = await getFirstPreferredLangCode();
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
await DesktopManager.init(platform.getVersion());
///: END:ONLY_INCLUDE_IN
@ -525,7 +525,7 @@ export function setupController(
* @param {Port} remotePort - The port provided by a new context.
*/
connectRemote = async (remotePort) => {
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
if (
DesktopManager.isDesktopEnabled() &&
OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()
@ -651,7 +651,7 @@ export function setupController(
// communication with page or other extension
connectExternal = (remotePort) => {
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
if (
DesktopManager.isDesktopEnabled() &&
OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()
@ -769,7 +769,7 @@ export function setupController(
Object.values(controller.approvalController.state.pendingApprovals).forEach(
({ id, type }) => {
switch (type) {
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
case MESSAGE_TYPE.SNAP_DIALOG_ALERT:
case MESSAGE_TYPE.SNAP_DIALOG_PROMPT:
controller.approvalController.accept(id, null);
@ -791,7 +791,7 @@ export function setupController(
updateBadge();
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
if (OVERRIDE_ORIGIN.DESKTOP !== overrides?.getOrigin?.()) {
controller.store.subscribe((state) => {
DesktopManager.setState(state);

View File

@ -724,7 +724,7 @@ export default class MetaMetricsController {
[MetaMetricsUserTrait.Theme]: metamaskState.theme || 'default',
[MetaMetricsUserTrait.TokenDetectionEnabled]:
metamaskState.useTokenDetection,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
[MetaMetricsUserTrait.DesktopEnabled]:
metamaskState.desktopEnabled || false,
///: END:ONLY_INCLUDE_IN

View File

@ -327,7 +327,7 @@ function buildDefaultProviderConfigState(): ProviderConfiguration {
};
} else if (
process.env.METAMASK_DEBUG ||
process.env.METAMASK_ENV === 'test'
process.env.METAMASK_ENVIRONMENT === 'test'
) {
return {
type: NETWORK_TYPES.GOERLI,

View File

@ -4,6 +4,6 @@ export * from './enums';
export * from './permission-log';
export * from './specifications';
export * from './selectors';
///: BEGIN:ONLY_INCLUDE_IN(flask)
export * from './flask/snap-permissions';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
export * from './snaps/snap-permissions';
///: END:ONLY_INCLUDE_IN

View File

@ -2,7 +2,7 @@ import {
constructPermission,
PermissionType,
} from '@metamask/permission-controller';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications } from '@metamask/snaps-controllers';
import { caveatSpecifications as snapsCaveatsSpecifications } from '@metamask/rpc-methods';
///: END:ONLY_INCLUDE_IN
@ -71,7 +71,7 @@ export const getCaveatSpecifications = ({ getIdentities }) => {
validateCaveatAccounts(caveat.value, getIdentities),
},
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
...snapsCaveatsSpecifications,
...snapsEndowmentCaveatSpecifications,
///: END:ONLY_INCLUDE_IN

View File

@ -1,4 +1,4 @@
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { handlers as permittedSnapMethods } from '@metamask/rpc-methods/dist/permitted';
///: END:ONLY_INCLUDE_IN
import { permissionRpcMethods } from '@metamask/permission-controller';
@ -72,7 +72,7 @@ export function createMethodMiddleware(hooks) {
};
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const snapHandlerMap = permittedSnapMethods.reduce((map, handler) => {
for (const methodName of handler.methodNames) {
map.set(methodName, handler);

View File

@ -1,7 +1,6 @@
import * as Sentry from '@sentry/browser';
import { Dedupe, ExtraErrorData } from '@sentry/integrations';
import { BuildType } from '../../../shared/constants/app';
import { FilterEvents } from './sentry-filter-events';
import extractEthjsErrorMessage from './extractEthjsErrorMessage';
@ -90,7 +89,7 @@ export default function setupSentry({ release, getState }) {
}
const environment =
METAMASK_BUILD_TYPE === BuildType.main
METAMASK_BUILD_TYPE === 'main'
? METAMASK_ENVIRONMENT
: `${METAMASK_ENVIRONMENT}-${METAMASK_BUILD_TYPE}`;

View File

@ -48,12 +48,12 @@ import {
SubjectMetadataController,
SubjectType,
} from '@metamask/subject-metadata-controller';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { RateLimitController } from '@metamask/rate-limit-controller';
import { NotificationController } from '@metamask/notification-controller';
///: END:ONLY_INCLUDE_IN
import SmartTransactionsController from '@metamask/smart-transactions-controller';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import {
CronjobController,
JsonSnapsRegistry,
@ -83,7 +83,7 @@ import { KeyringType } from '../../shared/constants/keyring';
import {
CaveatTypes,
RestrictedMethods,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
EndowmentPermissions,
ExcludedSnapPermissions,
ExcludedSnapEndowments,
@ -95,7 +95,7 @@ import { MILLISECOND, SECOND } from '../../shared/constants/time';
import {
ORIGIN_METAMASK,
MESSAGE_TYPE,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
SNAP_DIALOG_TYPES,
///: END:ONLY_INCLUDE_IN
POLLING_TOKEN_ENVIRONMENT_TYPES,
@ -115,8 +115,7 @@ import { STATIC_MAINNET_TOKEN_LIST } from '../../shared/constants/tokens';
import { getTokenValueParam } from '../../shared/lib/metamask-controller-utils';
import { isManifestV3 } from '../../shared/modules/mv3.utils';
import { hexToDecimal } from '../../shared/modules/conversion.utils';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { isMain, isFlask } from '../../shared/constants/environment';
///: BEGIN:ONLY_INCLUDE_IN(desktop)
// eslint-disable-next-line import/order
import { DesktopController } from '@metamask/desktop/dist/controllers/desktop';
///: END:ONLY_INCLUDE_IN
@ -131,7 +130,7 @@ import createDupeReqFilterMiddleware from './lib/createDupeReqFilterMiddleware';
import createLoggerMiddleware from './lib/createLoggerMiddleware';
import {
createMethodMiddleware,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
createSnapMethodMiddleware,
///: END:ONLY_INCLUDE_IN
} from './lib/rpc-method-middleware';
@ -175,7 +174,7 @@ import {
NOTIFICATION_NAMES,
PermissionLogController,
unrestrictedMethods,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
buildSnapEndowmentSpecifications,
buildSnapRestrictedMethodSpecifications,
///: END:ONLY_INCLUDE_IN
@ -786,7 +785,7 @@ export default class MetamaskController extends EventEmitter {
);
},
}),
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
...this.getSnapPermissionSpecifications(),
///: END:ONLY_INCLUDE_IN
},
@ -807,7 +806,7 @@ export default class MetamaskController extends EventEmitter {
subjectCacheLimit: 100,
});
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const snapExecutionServiceArgs = {
iframeUrl: new URL('https://execution.metamask.io/0.15.1/index.html'),
messenger: this.controllerMessenger.getRestricted({
@ -852,6 +851,9 @@ export default class MetamaskController extends EventEmitter {
],
});
const allowLocalSnaps = process.env.ALLOW_LOCAL_SNAPS;
const requireAllowlist = process.env.REQUIRE_SNAPS_ALLOWLIST;
this.snapController = new SnapController({
environmentEndowmentPermissions: Object.values(EndowmentPermissions),
excludedPermissions: {
@ -863,8 +865,8 @@ export default class MetamaskController extends EventEmitter {
messenger: snapControllerMessenger,
featureFlags: {
dappsCanUpdateSnaps: true,
allowLocalSnaps: isFlask,
requireAllowlist: isMain,
allowLocalSnaps,
requireAllowlist,
},
});
@ -933,8 +935,8 @@ export default class MetamaskController extends EventEmitter {
this.snapsRegistry = new JsonSnapsRegistry({
state: initState.SnapsRegistry,
messenger: snapsRegistryMessenger,
refetchOnAllowlistMiss: isMain,
failOnUnavailableRegistry: isMain,
refetchOnAllowlistMiss: requireAllowlist,
failOnUnavailableRegistry: requireAllowlist,
url: {
registry: 'https://acl.execution.metamask.io/latest/registry.json',
signature: 'https://acl.execution.metamask.io/latest/signature.json',
@ -943,6 +945,9 @@ export default class MetamaskController extends EventEmitter {
'0x025b65308f0f0fb8bc7f7ff87bfc296e0330eee5d3c1d1ee4a048b2fd6a86fa0a6',
});
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
this.desktopController = new DesktopController({
initState: initState.DesktopController,
});
@ -1374,11 +1379,13 @@ export default class MetamaskController extends EventEmitter {
SmartTransactionsController: this.smartTransactionsController,
NftController: this.nftController,
PhishingController: this.phishingController,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
SnapController: this.snapController,
CronjobController: this.cronjobController,
SnapsRegistry: this.snapsRegistry,
NotificationController: this.notificationController,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
DesktopController: this.desktopController.store,
///: END:ONLY_INCLUDE_IN
...resetOnRestartStore,
@ -1408,11 +1415,13 @@ export default class MetamaskController extends EventEmitter {
TokensController: this.tokensController,
SmartTransactionsController: this.smartTransactionsController,
NftController: this.nftController,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
SnapController: this.snapController,
CronjobController: this.cronjobController,
SnapsRegistry: this.snapsRegistry,
NotificationController: this.notificationController,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
DesktopController: this.desktopController.store,
///: END:ONLY_INCLUDE_IN
...resetOnRestartStore,
@ -1504,7 +1513,7 @@ export default class MetamaskController extends EventEmitter {
}
canUseHardwareWallets() {
return !isManifestV3 || process.env.CONF?.HARDWARE_WALLETS_MV3;
return !isManifestV3 || process.env.HARDWARE_WALLETS_MV3;
}
resetStates(resetMethods) {
@ -1517,7 +1526,7 @@ export default class MetamaskController extends EventEmitter {
});
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
/**
* Constructor helper for getting Snap permission specifications.
*/
@ -1659,7 +1668,7 @@ export default class MetamaskController extends EventEmitter {
getPermittedAccountsByOrigin,
);
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
// Record Snap metadata whenever a Snap is added to state.
this.controllerMessenger.subscribe(
`${this.snapController.name}:snapAdded`,
@ -2165,7 +2174,7 @@ export default class MetamaskController extends EventEmitter {
rejectPermissionsRequest: this.rejectPermissionsRequest,
...getPermissionBackgroundApiMethods(permissionController),
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
// snaps
removeSnapError: this.controllerMessenger.call.bind(
this.controllerMessenger,
@ -2189,6 +2198,8 @@ export default class MetamaskController extends EventEmitter {
),
dismissNotifications: this.dismissNotifications.bind(this),
markNotificationsAsRead: this.markNotificationsAsRead.bind(this),
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
// Desktop
getDesktopEnabled: this.desktopController.getDesktopEnabled.bind(
this.desktopController,
@ -2488,7 +2499,7 @@ export default class MetamaskController extends EventEmitter {
// clear permissions
this.permissionController.clearState();
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
// Clear snap state
this.snapController.clearState();
// Clear notification state
@ -2614,7 +2625,7 @@ export default class MetamaskController extends EventEmitter {
async _loginUser() {
try {
// Automatic login via config password
const password = process.env.CONF?.PASSWORD;
const password = process.env.PASSWORD;
if (password && !process.env.IN_TEST) {
await this.submitPassword(password);
}
@ -3543,7 +3554,7 @@ export default class MetamaskController extends EventEmitter {
if (subjectType === SubjectType.Internal) {
origin = ORIGIN_METAMASK;
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
else if (subjectType === SubjectType.Snap) {
origin = sender.snapId;
}
@ -3591,7 +3602,7 @@ export default class MetamaskController extends EventEmitter {
});
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
/**
* For snaps running in workers.
*
@ -3749,7 +3760,7 @@ export default class MetamaskController extends EventEmitter {
}),
);
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
engine.push(
createSnapMethodMiddleware(subjectType === SubjectType.Snap, {
getUnlockPromise: this.appStateController.getUnlockPromise.bind(
@ -4247,7 +4258,7 @@ export default class MetamaskController extends EventEmitter {
}
};
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
updateCaveat = (origin, target, caveatType, caveatValue) => {
try {
this.controllerMessenger.call(

View File

@ -194,7 +194,7 @@ export default class ExtensionPlatform {
let message = `Transaction ${nonce} failed! ${
errorMessage || txMeta.err.message
}`;
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
if (isNaN(nonce)) {
message = `Transaction failed! ${errorMessage || txMeta.err.message}`;
}

View File

@ -22,7 +22,7 @@ import { checkForLastErrorAndLog } from '../../shared/modules/browser-runtime.ut
import { SUPPORT_LINK } from '../../shared/lib/ui-utils';
import {
getErrorHtml,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
registerDesktopErrorActions,
///: END:ONLY_INCLUDE_IN
} from '../../shared/lib/error-utils';
@ -264,7 +264,7 @@ async function start() {
(
err,
store,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
backgroundConnection,
///: END:ONLY_INCLUDE_IN
) => {
@ -274,7 +274,7 @@ async function start() {
'troubleStarting',
err,
store,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
backgroundConnection,
///: END:ONLY_INCLUDE_IN
);
@ -302,7 +302,7 @@ async function start() {
displayCriticalError(
'troubleStarting',
err,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
undefined,
backgroundConnection,
///: END:ONLY_INCLUDE_IN
@ -345,7 +345,7 @@ function initializeUi(activeTab, connectionStream, cb) {
cb(
err,
null,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
backgroundConnection,
///: END:ONLY_INCLUDE_IN
);
@ -367,7 +367,7 @@ async function displayCriticalError(
errorKey,
err,
metamaskState,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
backgroundConnection,
///: END:ONLY_INCLUDE_IN
) {
@ -375,14 +375,14 @@ async function displayCriticalError(
errorKey,
SUPPORT_LINK,
metamaskState,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
err,
///: END:ONLY_INCLUDE_IN
);
container.innerHTML = html;
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
registerDesktopErrorActions(backgroundConnection, browser);
///: END:ONLY_INCLUDE_IN

214
builds.yml Normal file
View File

@ -0,0 +1,214 @@
# TODO(ritave): Add support for environments (<root>/development/build/constants.js:@ENVIRONMENT)
# TODO(ritave): Add support for build targets (<root>/development/build/constants.js:@BUILD_TARGETS)
# TODO(ritave): Warn if not all of declared variables have been defined / used
# The priority order of variable definitions (most important to least important):
# <hardcoded build code>; <environmental variables>; .metamaskprodrc; .metamaskrc; builds.yml:.buildTypes.<type>.env; builds.yml:.features.<feature>.env; builds.yml:.env
# The build type to use when no build type provided in the cli
default: &default main
# Declaration of build types
# Each build type is composed of features, env variables and assets.
# Also known as productFlavors in Android lingo
buildTypes:
main:
features:
- build-main
# Additional env variables that are specific to this build
env:
- INFURA_PROD_PROJECT_ID
- SEGMENT_PROD_WRITE_KEY
- INFURA_ENV_KEY_REF: INFURA_PROD_PROJECT_ID
- SEGMENT_WRITE_KEY_REF: SEGMENT_PROD_WRITE_KEY
beta:
features:
- build-beta
env:
- INFURA_BETA_PROJECT_ID
- SEGMENT_BETA_WRITE_KEY
- INFURA_ENV_KEY_REF: INFURA_BETA_PROJECT_ID
- SEGMENT_WRITE_KEY_REF: SEGMENT_BETA_WRITE_KEY
# Modifies how the version is displayed.
# eg. instead of 10.25.0 -> 10.25.0-beta.2
isPrerelease: true
# Folder which contains overrides to browser manifests
manifestOverrides: ./app/build-types/mmi/manifest/
flask:
# Code surrounded using code fences for that feature
# will not be removed
features:
- snaps
- desktop
- build-flask
env:
- INFURA_FLASK_PROJECT_ID
- SEGMENT_FLASK_WRITE_KEY
- ALLOW_LOCAL_SNAPS: true
- REQUIRE_SNAPS_ALLOWLIST: false
- SUPPORT_LINK: https://metamask-flask.zendesk.com/hc
- SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new
- INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID
- SEGMENT_WRITE_KEY_REF: SEGMENT_FLASK_WRITE_KEY
isPrerelease: true
desktop:
features:
- snaps
- desktop
- build-flask
env:
- INFURA_FLASK_PROJECT_ID
- SEGMENT_FLASK_WRITE_KEY
- ALLOW_LOCAL_SNAPS: true
- REQUIRE_SNAPS_ALLOWLIST: false
- SUPPORT_LINK: https://metamask-flask.zendesk.com/hc
- SUPPORT_REQUEST_LINK: https://metamask-flask.zendesk.com/hc/en-us/requests/new
- INFURA_ENV_KEY_REF: INFURA_FLASK_PROJECT_ID
- SEGMENT_WRITE_KEY_REF: SEGMENT_FLASK_WRITE_KEY
isPrerelease: true
mmi:
features:
- build-mmi
env:
- INFURA_MMI_PROJECT_ID
- SEGMENT_MMI_WRITE_KEY
- INFURA_ENV_KEY_REF: INFURA_MMI_PROJECT_ID
- SEGMENT_WRITE_KEY_REF: SEGMENT_MMI_WRITE_KEY
- SUPPORT_LINK: https://mmi-support.zendesk.com/hc/en-us
- SUPPORT_REQUEST_LINK: https://mmi-support.zendesk.com/hc/en-us/requests/new
# For some reason, MMI uses this type of versioning
# Leaving it on for backwards compatibility
isPrerelease: true
# Build types are composed of a set of features.
# Each feature can have code fences that add new code
# as well declaring, defining and overriding env variables
features:
snaps:
# Each feature might have variables that only exist when built with that feature active
env:
# Whether to allow snaps from localhost - local://localhost:8080/snap.manifest.json
# Enabled in Flask, will be disabled in Main
- ALLOW_LOCAL_SNAPS
# Whether to verify that a snap can be installed using an allow list
- REQUIRE_SNAPS_ALLOWLIST
assets:
- ./{app,shared,ui}/**/snaps/**
desktop:
env:
- COMPATIBILITY_VERSION_EXTENSION: 1
- DISABLE_WEB_SOCKET_ENCRYPTION: false
- SKIP_OTP_PAIRING_FLOW: false
- WEB_SOCKET_PORT: null
###
# Build Type code extensions. Things like different support links, warning pages, banners
###
build-main:
build-beta:
assets:
# Assets that will be copied
- src: ./app/build-types/beta/images/
dest: images
# Assets that are exclusively included in this feature and ignored in others
# Supports globs
- ./{app,shared,ui}/**/beta/**
build-mmi:
assets:
- src: ./app/build-types/mmi/images/
dest: images
- ./{app,shared,ui}/**/mmi/**
build-flask:
assets:
- src: ./app/build-types/flask/images/
dest: images
- ./{app,shared,ui}/**/flask/**
# Env variables that are required for all types of builds
#
# env object supports both declarations (- FOO), and definitions (- FOO: BAR).
# Variables that were declared have to be defined somewhere in the load chain before usage
env:
- SWAPS_USE_DEV_APIS: false
- PORTFOLIO_URL: https://portfolio.metamask.io
- TOKEN_ALLOWANCE_IMPROVEMENTS: false
- TRANSACTION_SECURITY_PROVIDER: false
# The unlock password
- PASSWORD: null
# Also see METAMASK_DEBUG and NODE_DEBUG
- DEBUG: null
- SUPPORT_LINK: https://support.metamask.io
- SUPPORT_REQUEST_LINK: https://metamask.zendesk.com/hc/en-us
- SKIP_BACKGROUND_INITIALIZATION: false
- MULTICHAIN: false
# TODO(ritave): Move ManifestV3 into a feature?
- ENABLE_MV3: false
- HARDWARE_WALLETS_MV3: false
# These are exclusively used for MV3
- APPLY_LAVAMOAT
- FILE_NAMES
###
# API keys to 3rd party services
###
- PUBNUB_PUB_KEY: null
- PUBNUB_SUB_KEY: null
- SEGMENT_HOST: null
- SENTRY_DSN: null
- SENTRY_DSN_DEV: null
- OPENSEA_KEY: null
- ETHERSCAN_KEY: null
# also INFURA_PROJECT_ID below
###
# Build system backwards compatibility
###
- INFURA_ENV_KEY_REF
- SEGMENT_WRITE_KEY_REF
###
# Variables that are modified with hardcoded code
###
# Used for debugging changes to the phishing warning page.
# Modified in <root>/development/build/scripts.js:@getPhishingWarningPageUrl
- PHISHING_WARNING_PAGE_URL: null
# Modified in <root>/development/build/scripts.js:@getInfuraProjectId
- INFURA_PROJECT_ID
# Modified in <root>/development/build/scripts.js:@getSegmentWriteKey
- SEGMENT_WRITE_KEY: ''
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
# Also see DEBUG and NODE_DEBUG
- METAMASK_DEBUG: false
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
- ICON_NAMES
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
- IN_TEST
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
- METAMASK_ENVIRONMENT
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
- METAMASK_VERSION
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
- METAMASK_BUILD_TYPE
# Modified in <root>/development/build/scripts.js:@setEnvironmentVariables
- NODE_ENV
# Defined by node itself
# For the purposes of the build system we define it as empty below
# if it's not inside process.env
# Also see DEBUG and METAMASK_DEBUG
- NODE_DEBUG: ''
###
# Meta variables
###
# Uses yaml anchors to DRY - https://juju.is/docs/sdk/yaml-anchors-and-aliases
- METAMASK_BUILD_TYPE_DEFAULT: *default

View File

@ -1,26 +1,13 @@
const path = require('path');
const { readFile } = require('fs/promises');
const assert = require('assert');
const { AssertionError } = require('assert');
const ini = require('ini');
const { BuildType } = require('../lib/build-type');
const { loadBuildTypesConfig } = require('../lib/build-type');
const { Variables } = require('../lib/variables');
const { ENVIRONMENT } = require('./constants');
const configurationPropertyNames = [
'MULTICHAIN',
'INFURA_PROJECT_ID',
'PHISHING_WARNING_PAGE_URL',
'PORTFOLIO_URL',
'SEGMENT_HOST',
'SEGMENT_WRITE_KEY',
'SENTRY_DSN_DEV',
'SWAPS_USE_DEV_APIS',
// Desktop
'COMPATIBILITY_VERSION_EXTENSION',
'DISABLE_WEB_SOCKET_ENCRYPTION',
'METAMASK_DEBUG',
'SKIP_OTP_PAIRING_FLOW',
'ENABLE_MV3',
];
const productionConfigurationPropertyNames = [
const VARIABLES_REQUIRED_IN_PRODUCTION = [
'INFURA_BETA_PROJECT_ID',
'INFURA_FLASK_PROJECT_ID',
'INFURA_PROD_PROJECT_ID',
@ -30,96 +17,145 @@ const productionConfigurationPropertyNames = [
'SENTRY_DSN',
];
/**
* Get configuration for non-production builds.
*
* @returns {object} The production configuration.
*/
async function getConfig() {
const configPath = path.resolve(__dirname, '..', '..', '.metamaskrc');
async function fromIniFile(filepath) {
let configContents = '';
try {
configContents = await readFile(configPath, {
configContents = await readFile(filepath, {
encoding: 'utf8',
});
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
return undefined;
}
const environmentVariables = {};
for (const propertyName of configurationPropertyNames) {
if (process.env[propertyName]) {
environmentVariables[propertyName] = process.env[propertyName];
const variables = ini.parse(configContents);
assert(
!Object.values(variables).some((variable) => typeof variable === 'object'),
`When loading ${filepath} - INI categories are not supported`,
);
const entries = Object.entries(variables);
const declarations = new Set(
entries.filter(([, value]) => value === '').map(([key]) => key),
);
const definitions = new Map(
entries
.filter(([, value]) => value !== '')
.map(([key, value]) => [key, value]),
);
return { declarations, definitions };
}
function fromEnv(declarations) {
const definitions = new Map(
[...declarations]
.filter((declaration) => declaration in process.env)
.map((declaration) => [declaration, process.env[declaration]]),
);
return { definitions, declarations: new Set() };
}
function fromBuildsYML(buildType, config) {
const extractDeclarations = (envArray) =>
envArray === undefined
? []
: envArray.map((env) => (typeof env === 'string' ? env : env.key));
const extractDefinitions = (envArray) =>
envArray === undefined
? []
: envArray.filter((env) => typeof env !== 'string');
// eslint-disable-next-line no-param-reassign
buildType = buildType ?? config.default;
const activeBuild = config.buildTypes[buildType];
const activeFeatures = activeBuild.features ?? [];
let declarations = [...extractDeclarations(config.env)];
activeFeatures
.map((feature) => config.features[feature])
.filter((feature) => feature !== null)
.forEach(({ env }) => declarations.push(...extractDeclarations(env)));
declarations.push(...extractDeclarations(activeBuild.env));
declarations = new Set(declarations);
const definitions = new Map();
// 1. root env
extractDefinitions(config.env).forEach(({ key, value }) =>
definitions.set(key, value),
);
// 2. features env
activeFeatures
.filter((key) => config.features[key] !== null)
.map((key) => config.features[key].env)
.map(extractDefinitions)
.flat()
.forEach(({ key, value }) => definitions.set(key, value));
// 3. build type env
extractDefinitions(activeBuild.env).forEach(({ key, value }) =>
definitions.set(key, value),
);
return { declarations, definitions, activeFeatures, activeBuild };
}
/**
*
* @param {string?} buildType - The chosen build type to build
* @param environment
* @returns Parsed configuration of the build pipeline
*/
async function getConfig(buildType, environment) {
const config = loadBuildTypesConfig();
const {
declarations: ymlDeclarations,
definitions: ymlDefinitions,
activeBuild,
activeFeatures,
} = await fromBuildsYML(buildType, config);
const variables = new Variables(ymlDeclarations);
// notice that maps have inverted value and key pair in forEach
ymlDefinitions.forEach((value, key) => variables.set(key, value));
(
await fromIniFile(path.resolve(__dirname, '..', '..', '.metamaskrc'))
)?.definitions.forEach((value, key) => variables.set(key, value));
(
await fromIniFile(path.resolve(__dirname, '..', '..', '.metamaskprodrc'))
)?.definitions.forEach((value, key) => variables.set(key, value));
fromEnv(ymlDeclarations).definitions.forEach((value, key) =>
variables.set(key, value),
);
// TODO(ritave): Move build targets and environments to builds.yml
if (environment === ENVIRONMENT.PRODUCTION) {
const undefinedVariables = VARIABLES_REQUIRED_IN_PRODUCTION.filter(
(variable) => !variables.isDefined(variable),
);
if (undefinedVariables.length !== 0) {
const message = `Some variables required to build production target are not defined.
- ${undefinedVariables.join('\n - ')}
`;
throw new AssertionError({ message });
}
}
return {
...ini.parse(configContents),
...environmentVariables,
variables,
activeBuild,
activeFeatures,
buildsYml: config,
};
}
/**
* Get configuration for production builds and perform validation.
*
* This function validates that all required variables are present, and that
* the production configuration file doesn't include any extraneous entries.
*
* @param {BuildType} buildType - The current build type (e.g. "main", "flask",
* etc.).
* @returns {object} The production configuration.
*/
async function getProductionConfig(buildType) {
const prodConfigPath = path.resolve(__dirname, '..', '..', '.metamaskprodrc');
let prodConfigContents = '';
try {
prodConfigContents = await readFile(prodConfigPath, {
encoding: 'utf8',
});
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const environmentVariables = {};
for (const propertyName of productionConfigurationPropertyNames) {
if (process.env[propertyName]) {
environmentVariables[propertyName] = process.env[propertyName];
}
}
const prodConfig = {
...ini.parse(prodConfigContents),
...environmentVariables,
};
const requiredEnvironmentVariables = {
all: ['SENTRY_DSN'],
[BuildType.beta]: ['INFURA_BETA_PROJECT_ID', 'SEGMENT_BETA_WRITE_KEY'],
[BuildType.flask]: ['INFURA_FLASK_PROJECT_ID', 'SEGMENT_FLASK_WRITE_KEY'],
[BuildType.main]: ['INFURA_PROD_PROJECT_ID', 'SEGMENT_PROD_WRITE_KEY'],
[BuildType.mmi]: ['INFURA_MMI_PROJECT_ID', 'SEGMENT_MMI_WRITE_KEY'],
};
for (const required of [
...requiredEnvironmentVariables.all,
...requiredEnvironmentVariables[buildType],
]) {
if (!prodConfig[required]) {
throw new Error(`Missing '${required}' environment variable`);
}
}
const allValid = Object.values(requiredEnvironmentVariables).flat();
for (const environmentVariable of Object.keys(prodConfig)) {
if (!allValid.includes(environmentVariable)) {
throw new Error(`Invalid environment variable: '${environmentVariable}'`);
}
}
return prodConfig;
}
module.exports = { getConfig, getProductionConfig };
module.exports = {
getConfig,
};

View File

@ -6,7 +6,7 @@ const del = require('del');
const pify = require('pify');
const pump = pify(require('pump'));
const { BuildType } = require('../lib/build-type');
const { loadBuildTypesConfig } = require('../lib/build-type');
const { TASKS } = require('./constants');
const { createTask, composeParallel } = require('./task');
@ -42,7 +42,7 @@ function createEtcTasks({ browserPlatforms, buildType, livereload, version }) {
function createZipTask(platform, buildType, version) {
return async () => {
const path =
buildType === BuildType.main
buildType === loadBuildTypesConfig().default
? `metamask-${platform}-${version}`
: `metamask-${buildType}-${platform}-${version}`;
await pump(

View File

@ -10,8 +10,10 @@ const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const { sync: globby } = require('globby');
const lavapack = require('@lavamoat/lavapack');
const difference = require('lodash/difference');
const { intersection } = require('lodash');
const { getVersion } = require('../lib/get-version');
const { BuildType, BuildTypeInheritance } = require('../lib/build-type');
const { loadBuildTypesConfig } = require('../lib/build-type');
const { TASKS, ENVIRONMENT } = require('./constants');
const {
createTask,
@ -275,9 +277,9 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
type: 'boolean',
})
.option('build-type', {
default: BuildType.main,
default: loadBuildTypesConfig().default,
description: 'The type of build to create.',
choices: Object.keys(BuildType),
choices: Object.keys(loadBuildTypesConfig().buildTypes),
})
.option('build-version', {
default: 0,
@ -380,29 +382,36 @@ testDev: Create an unoptimized, live-reloading build for debugging e2e tests.`,
* build, or `null` if no files are to be ignored.
*/
function getIgnoredFiles(currentBuildType) {
const inheritedBuildTypes = BuildTypeInheritance[currentBuildType] || [];
const excludedFiles = Object.values(BuildType)
// This filter removes "main" and the current build type. The files of any
// build types that remain in the array will be excluded. "main" is the
// default build type, and has no files that are excluded from other builds.
.filter(
(buildType) =>
buildType !== BuildType.main &&
buildType !== currentBuildType &&
!inheritedBuildTypes.includes(buildType),
)
// Compute globs targeting files for exclusion for each excluded build
// type.
.reduce((excludedGlobs, excludedBuildType) => {
return excludedGlobs.concat([
`../../app/**/${excludedBuildType}/**`,
`../../shared/**/${excludedBuildType}/**`,
`../../ui/**/${excludedBuildType}/**`,
]);
}, [])
// This creates absolute paths of the form:
// PATH_TO_REPOSITORY_ROOT/app/**/${excludedBuildType}/**
.map((pathGlob) => path.resolve(__dirname, pathGlob));
const buildConfig = loadBuildTypesConfig();
const cwd = process.cwd();
return globby(excludedFiles);
const exclusiveAssetsForFeatures = (features) =>
globby(
features
.flatMap(
(feature) =>
buildConfig.features[feature].assets
?.filter((asset) => 'exclusiveInclude' in asset)
.map((asset) => asset.exclusiveInclude) ?? [],
)
.map((pathGlob) => path.resolve(cwd, pathGlob)),
);
const allFeatures = Object.keys(buildConfig.features);
const activeFeatures =
buildConfig.buildTypes[currentBuildType].features ?? [];
const inactiveFeatures = difference(allFeatures, activeFeatures);
const ignoredPaths = exclusiveAssetsForFeatures(inactiveFeatures);
// We do a sanity check to verify that any inactive feature haven't excluded files
// that active features are trying to include
const activePaths = exclusiveAssetsForFeatures(activeFeatures);
const conflicts = intersection(activePaths, ignoredPaths);
if (conflicts.length !== 0) {
throw new Error(`Below paths are required exclusively by both active and inactive features resulting in a conflict:
\t-> ${conflicts.join('\n\t-> ')}
Please fix builds.yml`);
}
return ignoredPaths;
}

View File

@ -6,7 +6,7 @@ const { mergeWith, cloneDeep, capitalize } = require('lodash');
const baseManifest = process.env.ENABLE_MV3
? require('../../app/manifest/v3/_base.json')
: require('../../app/manifest/v2/_base.json');
const { BuildType } = require('../lib/build-type');
const { loadBuildTypesConfig } = require('../lib/build-type');
const { TASKS, ENVIRONMENT } = require('./constants');
const { createTask, composeSeries } = require('./task');
@ -165,25 +165,24 @@ async function writeJson(obj, file) {
* Get manifest modifications for the given build type, including modifications specific to the
* given platform.
*
* @param {BuildType} buildType - The build type.
* @param {string} buildType - The build type.
* @param {string} platform - The platform (i.e. the browser).
* @returns {object} The build modificantions for the given build type and platform.
* @returns {object} The build modifications for the given build type and platform.
*/
async function getBuildModifications(buildType, platform) {
if (!Object.values(BuildType).includes(buildType)) {
const buildConfig = loadBuildTypesConfig();
if (!(buildType in buildConfig.buildTypes)) {
throw new Error(`Invalid build type: ${buildType}`);
} else if (buildType === BuildType.main) {
}
const overridesPath = buildConfig.buildTypes[buildType].manifestOverrides;
if (overridesPath === undefined) {
return {};
}
const builtTypeManifestDirectoryPath = path.resolve(
__dirname,
'..',
'..',
'app',
'build-types',
buildType,
'manifest',
process.cwd(),
overridesPath,
);
const baseBuildTypeModificationsPath = path.join(

View File

@ -1,7 +1,10 @@
// TODO(ritave): Remove switches on hardcoded build types
const { callbackify } = require('util');
const path = require('path');
const { writeFileSync, readFileSync } = require('fs');
const EventEmitter = require('events');
const assert = require('assert');
const gulp = require('gulp');
const watch = require('gulp-watch');
const Vinyl = require('vinyl');
@ -29,10 +32,9 @@ const bifyModuleGroups = require('bify-module-groups');
const phishingWarningManifest = require('@metamask/phishing-warning/package.json');
const { streamFlatMap } = require('../stream-flat-map');
const { BuildType } = require('../lib/build-type');
const { generateIconNames } = require('../generate-icon-names');
const { BUILD_TARGETS, ENVIRONMENT } = require('./constants');
const { getConfig, getProductionConfig } = require('./config');
const { getConfig } = require('./config');
const {
isDevBuild,
isTestBuild,
@ -101,67 +103,79 @@ const standardScuttlingConfig = {
* Get the appropriate Infura project ID.
*
* @param {object} options - The Infura project ID options.
* @param {BuildType} options.buildType - The current build type.
* @param {object} options.config - The environment variable configuration.
* @param {string} options.buildType - The current build type.
* @param {ENVIRONMENT[keyof ENVIRONMENT]} options.environment - The build environment.
* @param {boolean} options.testing - Whether this is a test build or not.
* @param options.variables
* @returns {string} The Infura project ID.
*/
function getInfuraProjectId({ buildType, config, environment, testing }) {
function getInfuraProjectId({ buildType, variables, environment, testing }) {
if (testing) {
return '00000000000000000000000000000000';
} else if (environment !== ENVIRONMENT.PRODUCTION) {
// Skip validation because this is unset on PRs from forks.
return config.INFURA_PROJECT_ID;
} else if (buildType === BuildType.main) {
return config.INFURA_PROD_PROJECT_ID;
} else if (buildType === BuildType.beta) {
return config.INFURA_BETA_PROJECT_ID;
} else if (buildType === BuildType.flask) {
return config.INFURA_FLASK_PROJECT_ID;
} else if (buildType === BuildType.mmi) {
return config.INFURA_MMI_PROJECT_ID;
return variables.get('INFURA_PROJECT_ID');
}
throw new Error(`Invalid build type: '${buildType}'`);
/** @type {string|undefined} */
const infuraKeyReference = process.env.INFURA_ENV_KEY_REF;
assert(
typeof infuraKeyReference === 'string' && infuraKeyReference.length > 0,
`Build type "${buildType}" has improperly set INFURA_ENV_KEY_REF in builds.yml. Current value: "${infuraKeyReference}"`,
);
/** @type {string|undefined} */
const infuraProjectId = variables.get(infuraKeyReference);
assert(
typeof infuraProjectId === 'string' && infuraProjectId.length > 0,
`Infura Project ID environmental variable "${infuraKeyReference}" is set improperly.`,
);
return infuraProjectId;
}
/**
* Get the appropriate Segment write key.
*
* @param {object} options - The Segment write key options.
* @param {BuildType} options.buildType - The current build type.
* @param {object} options.config - The environment variable configuration.
* @param {string} options.buildType - The current build type.
* @param {keyof ENVIRONMENT} options.environment - The current build environment.
* @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline
* @returns {string} The Segment write key.
*/
function getSegmentWriteKey({ buildType, config, environment }) {
function getSegmentWriteKey({ buildType, variables, environment }) {
if (environment !== ENVIRONMENT.PRODUCTION) {
// Skip validation because this is unset on PRs from forks, and isn't necessary for development builds.
return config.SEGMENT_WRITE_KEY;
} else if (buildType === BuildType.main) {
return config.SEGMENT_PROD_WRITE_KEY;
} else if (buildType === BuildType.beta) {
return config.SEGMENT_BETA_WRITE_KEY;
} else if (buildType === BuildType.flask) {
return config.SEGMENT_FLASK_WRITE_KEY;
} else if (buildType === BuildType.mmi) {
return config.SEGMENT_MMI_WRITE_KEY;
return variables.get('SEGMENT_WRITE_KEY');
}
throw new Error(`Invalid build type: '${buildType}'`);
const segmentKeyReference = process.env.SEGMENT_WRITE_KEY_REF;
assert(
typeof segmentKeyReference === 'string' && segmentKeyReference.length > 0,
`Build type "${buildType}" has improperly set SEGMENT_WRITE_KEY_REF in builds.yml. Current value: "${segmentKeyReference}"`,
);
const segmentWriteKey = variables.get(segmentKeyReference);
assert(
typeof segmentWriteKey === 'string' && segmentWriteKey.length > 0,
`Segment Write Key environmental variable "${segmentKeyReference}" is set improperly.`,
);
return segmentWriteKey;
}
/**
* Get the URL for the phishing warning page, if it has been set.
*
* @param {object} options - The phishing warning page options.
* @param {object} options.config - The environment variable configuration.
* @param {boolean} options.testing - Whether this is a test build or not.
* @param {import('../lib/variables').Variables} options.variables - Object containing all variables that modify the build pipeline
* @returns {string} The URL for the phishing warning page, or `undefined` if no URL is set.
*/
function getPhishingWarningPageUrl({ config, testing }) {
let phishingWarningPageUrl = config.PHISHING_WARNING_PAGE_URL;
function getPhishingWarningPageUrl({ variables, testing }) {
let phishingWarningPageUrl = variables.get('PHISHING_WARNING_PAGE_URL');
if (!phishingWarningPageUrl) {
assert(
phishingWarningPageUrl === null ||
typeof phishingWarningPageUrl === 'string',
);
if (phishingWarningPageUrl === null) {
phishingWarningPageUrl = testing
? 'http://localhost:9999/'
: `https://metamask.github.io/phishing-warning/v${phishingWarningManifest.version}/`;
@ -209,7 +223,7 @@ module.exports = createScriptTasks;
* LavaMoat at runtime or not.
* @param {string[]} options.browserPlatforms - A list of browser platforms to
* build bundles for.
* @param {BuildType} options.buildType - The current build type (e.g. "main",
* @param {string} options.buildType - The current build type (e.g. "main",
* "flask", etc.).
* @param {string[] | null} options.ignoredFiles - A list of files to exclude
* from the current build.
@ -455,7 +469,7 @@ function createScriptTasks({
* @param {string[]} options.browserPlatforms - A list of browser platforms to
* build bundles for.
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
* @param {BuildType} options.buildType - The current build type (e.g. "main",
* @param {string} options.buildType - The current build type (e.g. "main",
* "flask", etc.).
* @param {string[] | null} options.ignoredFiles - A list of files to exclude
* from the current build.
@ -541,7 +555,7 @@ async function createManifestV3AppInitializationBundle({
* @param {string[]} options.browserPlatforms - A list of browser platforms to
* build bundles for.
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
* @param {BuildType} options.buildType - The current build type (e.g. "main",
* @param {string} options.buildType - The current build type (e.g. "main",
* "flask", etc.).
* @param {string[]} options.entryFiles - A list of entry point file paths,
* relative to the repository root directory.
@ -576,18 +590,29 @@ function createFactoredBuild({
const reloadOnChange = isDevBuild(buildTarget);
const minify = !isDevBuild(buildTarget);
const envVars = await getEnvironmentVariables({
const environment = getEnvironment({ buildTarget });
const config = await getConfig(buildType, environment);
const { variables, activeBuild } = config;
await setEnvironmentVariables({
buildTarget,
buildType,
environment,
variables,
activeBuild,
version,
});
const features = {
active: new Set(activeBuild.features ?? []),
all: new Set(Object.keys(config.buildsYml.features)),
};
setupBundlerDefaults(buildConfiguration, {
buildTarget,
buildType,
envVars,
variables,
envVars: buildSafeVariableObject(variables),
ignoredFiles,
policyOnly,
minify,
features,
reloadOnChange,
shouldLintFenceFiles,
});
@ -760,7 +785,7 @@ function createFactoredBuild({
* @param {string[]} options.browserPlatforms - A list of browser platforms to
* build the bundle for.
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
* @param {BuildType} options.buildType - The current build type (e.g. "main",
* @param {string} options.buildType - The current build type (e.g. "main",
* "flask", etc.).
* @param {string} options.destFilepath - The file path the bundle should be
* written to.
@ -806,20 +831,31 @@ function createNormalBundle({
const reloadOnChange = Boolean(devMode);
const minify = Boolean(devMode) === false;
const envVars = {
...(await getEnvironmentVariables({
buildTarget,
buildType,
version,
})),
...extraEnvironmentVariables,
const environment = getEnvironment({ buildTarget });
const config = await getConfig(buildType, environment);
const { activeBuild, variables } = config;
await setEnvironmentVariables({
buildTarget,
buildType,
variables,
environment,
activeBuild,
version,
});
Object.entries(extraEnvironmentVariables ?? {}).forEach(([key, value]) =>
variables.set(key, value),
);
const features = {
active: new Set(activeBuild.features ?? []),
all: new Set(Object.keys(config.buildsYml.features)),
};
setupBundlerDefaults(buildConfiguration, {
buildType,
envVars,
envVars: buildSafeVariableObject(variables),
ignoredFiles,
policyOnly,
minify,
features,
reloadOnChange,
shouldLintFenceFiles,
applyLavaMoat,
@ -865,11 +901,11 @@ function setupBundlerDefaults(
buildConfiguration,
{
buildTarget,
buildType,
envVars,
ignoredFiles,
policyOnly,
minify,
features,
reloadOnChange,
shouldLintFenceFiles,
applyLavaMoat,
@ -882,7 +918,7 @@ function setupBundlerDefaults(
// Source transforms
transform: [
// // Remove code that should be excluded from builds of the current type
createRemoveFencedCodeTransform(buildType, shouldLintFenceFiles),
createRemoveFencedCodeTransform(features, shouldLintFenceFiles),
// Transpile top-level code
[
babelify,
@ -1090,56 +1126,55 @@ async function createBundle(buildConfiguration, { reloadOnChange }) {
}
/**
* Get environment variables to inject in the current build.
* Sets environment variables to inject in the current build.
*
* @param {object} options - Build options.
* @param {BUILD_TARGETS} options.buildTarget - The current build target.
* @param {BuildType} options.buildType - The current build type (e.g. "main",
* @param {string} options.buildType - The current build type (e.g. "main",
* "flask", etc.).
* @param {string} options.version - The current version of the extension.
* @returns {object} A map of environment variables to inject.
* @param options.activeBuild
* @param options.variables
* @param options.environment
*/
async function getEnvironmentVariables({ buildTarget, buildType, version }) {
const environment = getEnvironment({ buildTarget });
const config =
environment === ENVIRONMENT.PRODUCTION
? await getProductionConfig(buildType)
: await getConfig();
async function setEnvironmentVariables({
buildTarget,
buildType,
activeBuild,
environment,
variables,
version,
}) {
const devMode = isDevBuild(buildTarget);
const testing = isTestBuild(buildTarget);
const iconNames = await generateIconNames();
return {
variables.set({
ICON_NAMES: iconNames,
MULTICHAIN: config.MULTICHAIN === '1',
CONF: devMode ? config : {},
ENABLE_MV3: config.ENABLE_MV3,
IN_TEST: testing,
INFURA_PROJECT_ID: getInfuraProjectId({
buildType,
config,
activeBuild,
variables,
environment,
testing,
}),
METAMASK_DEBUG: devMode || config.METAMASK_DEBUG === '1',
METAMASK_DEBUG: devMode || variables.getMaybe('METAMASK_DEBUG') === true,
METAMASK_ENVIRONMENT: environment,
METAMASK_VERSION: version,
METAMASK_BUILD_TYPE: buildType,
NODE_ENV: devMode ? ENVIRONMENT.DEVELOPMENT : ENVIRONMENT.PRODUCTION,
PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({ config, testing }),
PORTFOLIO_URL: config.PORTFOLIO_URL || 'https://portfolio.metamask.io',
SEGMENT_HOST: config.SEGMENT_HOST,
SEGMENT_WRITE_KEY: getSegmentWriteKey({ buildType, config, environment }),
SENTRY_DSN: config.SENTRY_DSN,
SENTRY_DSN_DEV: config.SENTRY_DSN_DEV,
SWAPS_USE_DEV_APIS: config.SWAPS_USE_DEV_APIS === '1',
TOKEN_ALLOWANCE_IMPROVEMENTS: config.TOKEN_ALLOWANCE_IMPROVEMENTS === '1',
TRANSACTION_SECURITY_PROVIDER: config.TRANSACTION_SECURITY_PROVIDER === '1',
// Desktop
COMPATIBILITY_VERSION_EXTENSION: config.COMPATIBILITY_VERSION_EXTENSION,
DISABLE_WEB_SOCKET_ENCRYPTION: config.DISABLE_WEB_SOCKET_ENCRYPTION === '1',
SKIP_OTP_PAIRING_FLOW: config.SKIP_OTP_PAIRING_FLOW === '1',
};
PHISHING_WARNING_PAGE_URL: getPhishingWarningPageUrl({
variables,
testing,
}),
SEGMENT_WRITE_KEY: getSegmentWriteKey({
buildType,
activeBuild,
variables,
environment,
}),
});
}
function renderHtmlFile({
@ -1172,6 +1207,29 @@ function renderHtmlFile({
});
}
/**
* Builds a javascript object that throws an error when trying to access a property that wasn't defined properly
*
* @param {Variables} variables
* @returns {{[key: string]: unknown }} Variable definitions
*/
function buildSafeVariableObject(variables) {
return new Proxy(
{},
{
has(_, key) {
return key !== '_'; // loose-envify uses "_" for settings
},
get(_, key) {
if (key === '_') {
return undefined; // loose-envify uses "_" for settings
}
return variables.get(key);
},
},
);
}
function beep() {
process.stdout.write('\x07');
}

View File

@ -4,7 +4,7 @@ const watch = require('gulp-watch');
const glob = require('fast-glob');
const locales = require('../../app/_locales/index.json');
const { BuildType } = require('../lib/build-type');
const { loadBuildTypesConfig } = require('../lib/build-type');
const { TASKS } = require('./constants');
const { createTask, composeSeries } = require('./task');
@ -31,41 +31,23 @@ module.exports = function createStaticAssetTasks({
copyTargetsDevs[browser] = copyTargetsDev;
});
const additionalBuildTargets = {
[BuildType.beta]: [
{
src: './app/build-types/beta/images/',
dest: `images`,
},
],
[BuildType.flask]: [
{
src: './app/build-types/flask/images/',
dest: `images`,
},
],
[BuildType.desktop]: [
{
src: './app/build-types/desktop/images/',
dest: `images`,
},
],
[BuildType.mmi]: [
{
src: './app/build-types/mmi/images/',
dest: `images`,
},
],
};
const buildConfig = loadBuildTypesConfig();
if (Object.keys(additionalBuildTargets).includes(buildType)) {
Object.entries(copyTargetsProds).forEach(([_, copyTargetsProd]) =>
copyTargetsProd.push(...additionalBuildTargets[buildType]),
);
Object.entries(copyTargetsDevs).forEach(([_, copyTargetsDev]) =>
copyTargetsDev.push(...additionalBuildTargets[buildType]),
);
}
const activeFeatures = buildConfig.buildTypes[buildType].features ?? [];
const additionalAssets = activeFeatures.flatMap(
(feature) =>
buildConfig.features[feature].assets?.filter(
(asset) => !('exclusiveInclude' in asset),
) ?? [],
);
Object.entries(copyTargetsProds).forEach(([_, copyTargetsProd]) =>
copyTargetsProd.push(...additionalAssets),
);
Object.entries(copyTargetsDevs).forEach(([_, copyTargetsDev]) =>
copyTargetsDev.push(...additionalAssets),
);
const prodTasks = [];
Object.entries(copyTargetsProds).forEach(([browser, copyTargetsProd]) => {
@ -120,7 +102,7 @@ module.exports = function createStaticAssetTasks({
await Promise.all(
sources.map(async (src) => {
const relativePath = path.relative(baseDir, src);
await fs.copy(src, `${dest}${relativePath}`);
await fs.copy(src, `${dest}${relativePath}`, { overwrite: true });
}),
);
}

View File

@ -29,7 +29,7 @@ this.store.updateStructure({
...,
GasFeeController: this.gasFeeController,
TokenListController: this.tokenListController,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
SnapController: this.snapController,
///: END:ONLY_INCLUDE_IN
});
@ -49,7 +49,7 @@ Note that multiple build types can be specified by separating them with
commands inside the parameter parentheses:
```javascript
///: BEGIN:ONLY_INCLUDE_IN(beta,flask)
///: BEGIN:ONLY_INCLUDE_IN(build-beta,build-flask)
```
### Gotchas
@ -114,7 +114,7 @@ only be included in a particular build type, say `beta`, they should be wrapped
as follows:
```javascript
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
console.log('I am only included in beta builds.');
///: END:ONLY_INCLUDE_IN
```

View File

@ -0,0 +1,4 @@
export type Features = {
active: ReadonlySet<string>;
all: ReadonlySet<string>;
};

View File

@ -1,6 +1,5 @@
const path = require('path');
const { PassThrough, Transform } = require('stream');
const { BuildType, BuildTypeInheritance } = require('../../lib/build-type');
const { lintTransformedFile } = require('./utils');
const hasKey = (obj, key) => Reflect.hasOwnProperty.call(obj, key);
@ -18,14 +17,14 @@ class RemoveFencedCodeTransform extends Transform {
* Optionally lints the file if it was modified.
*
* @param {string} filePath - The path to the file being transformed.
* @param {string} buildType - The type of the current build process.
* @param {import('./remove-fenced-code').Features} features - Features that are currently enabled.
* @param {boolean} shouldLintTransformedFiles - Whether the file should be
* linted if modified by the transform.
*/
constructor(filePath, buildType, shouldLintTransformedFiles) {
constructor(filePath, features, shouldLintTransformedFiles) {
super();
this.filePath = filePath;
this.buildType = buildType;
this.features = features;
this.shouldLintTransformedFiles = shouldLintTransformedFiles;
this._fileBuffers = [];
}
@ -45,7 +44,7 @@ class RemoveFencedCodeTransform extends Transform {
try {
[fileContent, didModify] = removeFencedCode(
this.filePath,
this.buildType,
this.features,
Buffer.concat(this._fileBuffers).toString('utf8'),
);
} catch (error) {
@ -82,20 +81,14 @@ class RemoveFencedCodeTransform extends Transform {
* file is ignored by ESLint, since linting is our first line of defense against
* making un-syntactic modifications to files using code fences.
*
* @param {string} buildType - The type of the current build.
* @param {import('./remove-fenced-code').Features} features - Features that are currently enabled.
* @param {boolean} shouldLintTransformedFiles - Whether to lint transformed files.
* @returns {(filePath: string) => Transform} The transform function.
*/
function createRemoveFencedCodeTransform(
buildType,
features,
shouldLintTransformedFiles = true,
) {
if (!hasKey(BuildType, buildType)) {
throw new Error(
`Code fencing transform received unrecognized build type "${buildType}".`,
);
}
// Browserify transforms are functions that receive a file name and return a
// duplex stream. The stream receives the file contents piecemeal in the form
// of Buffers.
@ -117,7 +110,7 @@ function createRemoveFencedCodeTransform(
return new RemoveFencedCodeTransform(
filePath,
buildType,
features,
shouldLintTransformedFiles,
);
};
@ -132,31 +125,39 @@ const DirectiveCommands = {
ONLY_INCLUDE_IN: 'ONLY_INCLUDE_IN',
};
const CommandValidators = {
[DirectiveCommands.ONLY_INCLUDE_IN]: (params, filePath) => {
if (!params || params.length === 0) {
throw new Error(
getInvalidParamsMessage(
filePath,
DirectiveCommands.ONLY_INCLUDE_IN,
`No params specified.`,
),
);
}
params.forEach((param) => {
if (!hasKey(BuildType, param)) {
/**
* Factory function for command validators.
*
* @param {{ features: import('./remove-fenced-code').Features }} config - Configuration required for validation.
* @returns A mapping of command -> validator function.
*/
function CommandValidators({ features }) {
return {
[DirectiveCommands.ONLY_INCLUDE_IN]: (params, filePath) => {
if (!params || params.length === 0) {
throw new Error(
getInvalidParamsMessage(
filePath,
DirectiveCommands.ONLY_INCLUDE_IN,
`"${param}" is not a valid build type.`,
`No params specified.`,
),
);
}
});
},
};
for (const param of params) {
if (!features.all.has(param)) {
throw new Error(
getInvalidParamsMessage(
filePath,
DirectiveCommands.ONLY_INCLUDE_IN,
`"${param}" is not a declared build feature.`,
),
);
}
}
},
};
}
// Matches lines starting with "///:", and any preceding whitespace, except
// newlines. We except newlines to avoid eating blank lines preceding a fenced
@ -171,7 +172,8 @@ const fenceSentinelRegex = /^\s*\/\/\/:/u;
// At this stage of parsing, we are looking for one of:
// - TERMINUS:COMMAND(PARAMS)
// - TERMINUS:COMMAND
const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
const directiveParsingRegex =
/^([A-Z]+):([A-Z_]+)(?:\(((?:\w[-\w]*,)*\w[-\w]*)\))?$/u;
/**
* Removes fenced code from the given JavaScript source string. "Fenced code"
@ -186,7 +188,7 @@ const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
* Here's an example of a valid fence:
*
* ```javascript
* ///: BEGIN:ONLY_INCLUDE_IN(flask)
* ///: BEGIN:ONLY_INCLUDE_IN(build-flask)
* console.log('I am Flask.');
* ///: END:ONLY_INCLUDE_IN
* ```
@ -194,12 +196,12 @@ const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
* For details, please see the documentation.
*
* @param {string} filePath - The path to the file being transformed.
* @param {string} typeOfCurrentBuild - The type of the current build.
* @param {import('./remove-fenced-code').Features} features - Features that are currently active.
* @param {string} fileContent - The contents of the file being transformed.
* @returns {[string, modified]} A tuple of the post-transform file contents and
* a boolean indicating whether they were modified.
*/
function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) {
function removeFencedCode(filePath, features, fileContent) {
// Do not modify the file if we detect an inline sourcemap. For reasons
// yet to be determined, the transform receives every file twice while in
// watch mode, the second after Babel has transpiled the file. Babel adds
@ -318,6 +320,7 @@ function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) {
const splicingIndices = [];
let shouldSplice = false;
let currentCommand;
const commandValidators = CommandValidators({ features });
for (let i = 0; i < parsedDirectives.length; i++) {
const { line, indices, terminus, command, parameters } =
@ -335,21 +338,17 @@ function removeFencedCode(filePath, typeOfCurrentBuild, fileContent) {
currentCommand = command;
// Throws an error if the command parameters are invalid
CommandValidators[command](parameters, filePath);
commandValidators[command](parameters, filePath);
const validBuildTypes = [
typeOfCurrentBuild,
...(BuildTypeInheritance[typeOfCurrentBuild] || []),
];
const buildTypeMatches = validBuildTypes.some((validBuildType) =>
parameters.includes(validBuildType),
const blockIsActive = parameters.some((param) =>
features.active.has(param),
);
if (buildTypeMatches) {
if (blockIsActive) {
shouldSplice = false;
} else {
shouldSplice = true;
// Add start index of BEGIN directive line to splicing indices
splicingIndices.push(indices[0]);
}

View File

@ -1,5 +1,5 @@
const deepFreeze = require('deep-freeze-strict');
const { BuildType } = require('../../lib/build-type');
const { loadBuildTypesConfig } = require('../../lib/build-type');
const {
createRemoveFencedCodeTransform,
removeFencedCode,
@ -14,12 +14,22 @@ jest.mock('./utils', () => ({
// file because it takes up a lot of lines and is very distracting.
const testData = getTestData();
const getMinimalFencedCode = (params = 'flask') =>
const MAIN_BUILD = 'build-main';
const FLASK_BUILD = 'build-flask';
const getMinimalFencedCode = (params = FLASK_BUILD) =>
`///: BEGIN:ONLY_INCLUDE_IN(${params})
Conditionally_Included
///: END:ONLY_INCLUDE_IN
`;
const getFeatures = ({ all, active }) => ({
all: new Set(all),
active: new Set(active),
});
const buildTypesConfig = loadBuildTypesConfig();
describe('build/transforms/remove-fenced-code', () => {
describe('createRemoveFencedCodeTransform', () => {
const { lintTransformedFile: lintTransformedFileMock } = transformUtils;
@ -29,15 +39,14 @@ describe('build/transforms/remove-fenced-code', () => {
lintTransformedFileMock.mockImplementation(() => Promise.resolve());
});
it('rejects invalid build types', () => {
expect(() => createRemoveFencedCodeTransform('foobar')).toThrow(
/received unrecognized build type "foobar".$/u,
);
});
it('returns a PassThrough stream for files with ignored extensions', async () => {
const fileContent = '"Valid JSON content"\n';
const stream = createRemoveFencedCodeTransform('main')('file.json');
const stream = createRemoveFencedCodeTransform(
getFeatures({
active: [MAIN_BUILD],
all: [MAIN_BUILD],
}),
)('file.json');
let streamOutput = '';
await new Promise((resolve) => {
@ -60,7 +69,12 @@ describe('build/transforms/remove-fenced-code', () => {
const filePrefix = '// A comment\n';
const fileContent = filePrefix.concat(getMinimalFencedCode());
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
const stream = createRemoveFencedCodeTransform(
getFeatures({
active: [MAIN_BUILD],
all: [MAIN_BUILD, FLASK_BUILD],
}),
)(mockJsFileName);
let streamOutput = '';
await new Promise((resolve) => {
@ -92,7 +106,12 @@ describe('build/transforms/remove-fenced-code', () => {
.filter((line) => line !== '')
.map((line) => `${line}\n`);
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
const stream = createRemoveFencedCodeTransform(
getFeatures({
active: [MAIN_BUILD],
all: [MAIN_BUILD, FLASK_BUILD],
}),
)(mockJsFileName);
let streamOutput = '';
await new Promise((resolve) => {
@ -116,9 +135,14 @@ describe('build/transforms/remove-fenced-code', () => {
});
it('handles file with fences that is unmodified by the transform', async () => {
const fileContent = getMinimalFencedCode('main');
const fileContent = getMinimalFencedCode(MAIN_BUILD);
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
const stream = createRemoveFencedCodeTransform(
getFeatures({
active: [MAIN_BUILD],
all: [MAIN_BUILD],
}),
)(mockJsFileName);
let streamOutput = '';
await new Promise((resolve) => {
@ -141,7 +165,7 @@ describe('build/transforms/remove-fenced-code', () => {
const fileContent = filePrefix.concat(getMinimalFencedCode());
const stream = createRemoveFencedCodeTransform(
'main',
getFeatures({ all: [MAIN_BUILD, FLASK_BUILD], active: [MAIN_BUILD] }),
false,
)(mockJsFileName);
let streamOutput = '';
@ -166,7 +190,9 @@ describe('build/transforms/remove-fenced-code', () => {
'///: END:ONLY_INCLUDE_IN',
);
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
const stream = createRemoveFencedCodeTransform(
getFeatures({ all: [MAIN_BUILD, FLASK_BUILD], active: [MAIN_BUILD] }),
)(mockJsFileName);
await new Promise((resolve) => {
stream.on('error', (error) => {
@ -191,7 +217,9 @@ describe('build/transforms/remove-fenced-code', () => {
const filePrefix = '// A comment\n';
const fileContent = filePrefix.concat(getMinimalFencedCode());
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
const stream = createRemoveFencedCodeTransform(
getFeatures({ all: [FLASK_BUILD], active: [] }),
)(mockJsFileName);
await new Promise((resolve) => {
stream.on('error', (error) => {
@ -213,12 +241,15 @@ describe('build/transforms/remove-fenced-code', () => {
const mockFileName = 'file.js';
// Valid inputs
Object.keys(BuildType).forEach((buildType) => {
['main', 'flask', 'beta'].forEach((buildType) => {
const active = buildTypesConfig.buildTypes[buildType].features;
const all = Object.keys(buildTypesConfig.features);
const features = getFeatures({ all, active });
it(`transforms file with fences for build type "${buildType}"`, () => {
expect(
removeFencedCode(
mockFileName,
buildType,
features,
testData.validInputs.withFences,
),
).toStrictEqual(testData.validOutputs[buildType]);
@ -226,15 +257,15 @@ describe('build/transforms/remove-fenced-code', () => {
expect(
removeFencedCode(
mockFileName,
buildType,
features,
testData.validInputs.extraContentWithFences,
),
).toStrictEqual(testData.validOutputsWithExtraContent[buildType]);
// Ensure that the minimal input template is in fact valid
const minimalInput = getMinimalFencedCode(buildType);
const minimalInput = getMinimalFencedCode(`build-${buildType}`);
expect(
removeFencedCode(mockFileName, buildType, minimalInput),
removeFencedCode(mockFileName, features, minimalInput),
).toStrictEqual([minimalInput, false]);
});
@ -242,7 +273,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(
removeFencedCode(
mockFileName,
buildType,
features,
testData.validInputs.withoutFences,
),
).toStrictEqual([testData.validInputs.withoutFences, false]);
@ -250,7 +281,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(
removeFencedCode(
mockFileName,
buildType,
features,
testData.validInputs.extraContentWithoutFences,
),
).toStrictEqual([
@ -265,29 +296,17 @@ describe('build/transforms/remove-fenced-code', () => {
expect(
removeFencedCode(
mockFileName,
BuildType.flask,
getMinimalFencedCode('main'),
getFeatures({
active: [FLASK_BUILD],
all: [FLASK_BUILD, MAIN_BUILD],
}),
getMinimalFencedCode(MAIN_BUILD),
),
).toStrictEqual(['', true]);
});
it('keeps fences with inherited build types', () => {
// Desktop inherits from the flask build type
const minimalCode =
getMinimalFencedCode(BuildType.flask) +
getMinimalFencedCode(BuildType.desktop);
expect(
removeFencedCode(mockFileName, BuildType.desktop, minimalCode),
).toStrictEqual([minimalCode, false]);
expect(
removeFencedCode(mockFileName, BuildType.flask, minimalCode),
).toStrictEqual([getMinimalFencedCode(BuildType.flask), true]);
});
it('ignores sentinels preceded by non-whitespace', () => {
const validBeginDirective = '///: BEGIN:ONLY_INCLUDE_IN(flask)\n';
const validBeginDirective = '///: BEGIN:ONLY_INCLUDE_IN(build-flask)\n';
const ignoredLines = [
`a ${validBeginDirective}`,
`2 ${validBeginDirective}`,
@ -299,8 +318,11 @@ describe('build/transforms/remove-fenced-code', () => {
expect(
removeFencedCode(
mockFileName,
BuildType.flask,
getMinimalFencedCode('main').concat(ignoredLine),
getFeatures({
active: [FLASK_BUILD],
all: [FLASK_BUILD, MAIN_BUILD],
}),
getMinimalFencedCode(MAIN_BUILD).concat(ignoredLine),
),
).toStrictEqual([ignoredLine, true]);
@ -311,7 +333,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
modifiedInputWithoutFences,
),
).toStrictEqual([modifiedInputWithoutFences, false]);
@ -341,7 +363,11 @@ describe('build/transforms/remove-fenced-code', () => {
inputs.forEach((input) => {
expect(() =>
removeFencedCode(mockFileName, BuildType.flask, input),
removeFencedCode(
mockFileName,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
input,
),
).toThrow(
`Empty fence found in file "${mockFileName}":\n${emptyFence}`,
);
@ -368,7 +394,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode().replace(
fenceSentinelAndTerminusRegex,
replacement,
@ -382,53 +408,53 @@ describe('build/transforms/remove-fenced-code', () => {
it('rejects malformed BEGIN directives', () => {
// This is the first line of the minimal input template
const directiveString = '///: BEGIN:ONLY_INCLUDE_IN(flask)';
const directiveString = '///: BEGIN:ONLY_INCLUDE_IN(build-flask)';
const replacements = [
// Invalid terminus
'///: BE_GIN:ONLY_INCLUDE_IN(flask)',
'///: BE6IN:ONLY_INCLUDE_IN(flask)',
'///: BEGIN7:ONLY_INCLUDE_IN(flask)',
'///: BeGIN:ONLY_INCLUDE_IN(flask)',
'///: BE3:ONLY_INCLUDE_IN(flask)',
'///: BEG-IN:ONLY_INCLUDE_IN(flask)',
'///: BEG N:ONLY_INCLUDE_IN(flask)',
'///: BE_GIN:BEGIN:ONLY_INCLUDE_IN(build-flask)',
'///: BE6IN:BEGIN:ONLY_INCLUDE_IN(build-flask)',
'///: BEGIN7:BEGIN:ONLY_INCLUDE_IN(build-flask)',
'///: BeGIN:ONLY_INCLUDE_IN(build-flask)',
'///: BE3:BEGIN:ONLY_INCLUDE_IN(build-flask)',
'///: BEG-IN:BEGIN:ONLY_INCLUDE_IN(build-flask)',
'///: BEG N:BEGIN:ONLY_INCLUDE_IN(build-flask)',
// Invalid commands
'///: BEGIN:ONLY-INCLUDE_IN(flask)',
'///: BEGIN:ONLY_INCLUDE:IN(flask)',
'///: BEGIN:ONL6_INCLUDE_IN(flask)',
'///: BEGIN:ONLY_IN@LUDE_IN(flask)',
'///: BEGIN:ONLy_INCLUDE_IN(flask)',
'///: BEGIN:ONLy_INCLUDE_IN(build-flask)',
'///: BEGIN:ONLY INCLUDE_IN(flask)',
// Invalid parameters
'///: BEGIN:ONLY_INCLUDE_IN(,flask)',
'///: BEGIN:ONLY_INCLUDE_IN(flask,)',
'///: BEGIN:ONLY_INCLUDE_IN(flask,,main)',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask,)',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask,,main)',
'///: BEGIN:ONLY_INCLUDE_IN(,)',
'///: BEGIN:ONLY_INCLUDE_IN()',
'///: BEGIN:ONLY_INCLUDE_IN( )',
'///: BEGIN:ONLY_INCLUDE_IN(flask]',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask]',
'///: BEGIN:ONLY_INCLUDE_IN[flask)',
'///: BEGIN:ONLY_INCLUDE_IN(flask.main)',
'///: BEGIN:ONLY_INCLUDE_IN(flask,@)',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask.main)',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask,@)',
'///: BEGIN:ONLY_INCLUDE_IN(fla k)',
// Stuff after the directive
'///: BEGIN:ONLY_INCLUDE_IN(flask) A',
'///: BEGIN:ONLY_INCLUDE_IN(flask) 9',
'///: BEGIN:ONLY_INCLUDE_IN(flask)A',
'///: BEGIN:ONLY_INCLUDE_IN(flask)9',
'///: BEGIN:ONLY_INCLUDE_IN(flask)_',
'///: BEGIN:ONLY_INCLUDE_IN(flask))',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask) A',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask) 9',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)A',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)9',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)_',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask))',
];
replacements.forEach((replacement) => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode().replace(directiveString, replacement),
),
).toThrow(
@ -474,7 +500,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode().replace(directiveString, replacement),
),
).toThrow(
@ -488,14 +514,14 @@ describe('build/transforms/remove-fenced-code', () => {
it('rejects files with uneven number of fence lines', () => {
const additions = [
'///: BEGIN:ONLY_INCLUDE_IN(flask)',
'///: BEGIN:ONLY_INCLUDE_IN(build-flask)',
'///: END:ONLY_INCLUDE_IN',
];
additions.forEach((addition) => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode().concat(addition),
),
).toThrow(
@ -515,7 +541,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode().replace(validTerminus, replacement),
),
).toThrow(
@ -539,7 +565,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode().replace(validCommand, replacement),
),
).toThrow(
@ -557,10 +583,30 @@ describe('build/transforms/remove-fenced-code', () => {
it('rejects invalid command parameters', () => {
const testCases = [
['bar', ['bar', 'flask,bar', 'flask,beta,main,bar']],
['Foo', ['Foo', 'flask,Foo', 'flask,beta,main,Foo']],
['b3ta', ['b3ta', 'flask,b3ta', 'flask,beta,main,b3ta']],
['bEta', ['bEta', 'flask,bEta', 'flask,beta,main,bEta']],
[
'bar',
['bar', 'build-flask,bar', 'build-flask,build-beta,build-main,bar'],
],
[
'Foo',
['Foo', 'build-flask,Foo', 'build-flask,build-beta,build-main,Foo'],
],
[
'b3ta',
[
'b3ta',
'build-flask,b3ta',
'build-flask,build-beta,build-main,b3ta',
],
],
[
'bEta',
[
'bEta',
'build-flask,bEta',
'build-flask,build-beta,build-main,bEta',
],
],
];
testCases.forEach(([invalidParam, replacements]) => {
@ -568,11 +614,17 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({
active: [FLASK_BUILD],
all: [FLASK_BUILD, MAIN_BUILD, 'build-beta'],
}),
getMinimalFencedCode(replacement),
),
).toThrow(
new RegExp(`"${invalidParam}" is not a valid build type.$`, 'u'),
new RegExp(
`"${invalidParam}" is not a declared build feature.$`,
'u',
),
);
});
});
@ -581,7 +633,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
getMinimalFencedCode('').replace('()', ''),
),
).toThrow(/No params specified.$/u);
@ -589,7 +641,9 @@ describe('build/transforms/remove-fenced-code', () => {
it('rejects directive pairs with wrong terminus order', () => {
// We need more than one directive pair for this test
const input = getMinimalFencedCode().concat(getMinimalFencedCode('beta'));
const input = getMinimalFencedCode().concat(
getMinimalFencedCode('build-beta'),
);
const expectedBeginError =
'The first directive of a pair must be a "BEGIN" directive.';
@ -597,17 +651,17 @@ describe('build/transforms/remove-fenced-code', () => {
'The second directive of a pair must be an "END" directive.';
const testCases = [
[
'BEGIN:ONLY_INCLUDE_IN(flask)',
'BEGIN:ONLY_INCLUDE_IN(build-flask)',
'END:ONLY_INCLUDE_IN',
expectedBeginError,
],
[
/END:ONLY_INCLUDE_IN/mu,
'BEGIN:ONLY_INCLUDE_IN(main)',
'BEGIN:ONLY_INCLUDE_IN(build-main)',
expectedEndError,
],
[
'BEGIN:ONLY_INCLUDE_IN(beta)',
'BEGIN:ONLY_INCLUDE_IN(build-beta)',
'END:ONLY_INCLUDE_IN',
expectedBeginError,
],
@ -617,7 +671,7 @@ describe('build/transforms/remove-fenced-code', () => {
expect(() =>
removeFencedCode(
mockFileName,
BuildType.flask,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
input.replace(target, replacement),
),
).toThrow(expectedError);
@ -631,7 +685,11 @@ describe('build/transforms/remove-fenced-code', () => {
'\n//# sourceMappingURL=as32e32wcwc2234f2ew32cnin4243f4nv9nsdoivnxzoivnd',
);
expect(
removeFencedCode(mockFileName, BuildType.flask, input),
removeFencedCode(
mockFileName,
getFeatures({ active: [FLASK_BUILD], all: [FLASK_BUILD] }),
input,
),
).toStrictEqual([input, false]);
});
@ -644,14 +702,14 @@ function getTestData() {
const data = {
validInputs: {
withFences: `
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
Conditionally_Included
@ -660,7 +718,7 @@ Conditionally_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
@ -678,7 +736,7 @@ Conditionally_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
@ -690,14 +748,14 @@ Conditionally_Included
`,
extraContentWithFences: `
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
Conditionally_Included
@ -706,7 +764,7 @@ Conditionally_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
@ -723,7 +781,7 @@ Conditionally_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
@ -781,14 +839,14 @@ Always_Included
validOutputs: {
beta: [
`
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
Conditionally_Included
@ -810,14 +868,14 @@ Always_Included
],
flask: [
`
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
Conditionally_Included
@ -826,7 +884,7 @@ Conditionally_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
@ -836,16 +894,25 @@ Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(desktop)
Conditionally_Included
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
Conditionally_Included
Conditionally_Included
///: END:ONLY_INCLUDE_IN
`,
true,
false,
],
mmi: [
`
@ -872,14 +939,14 @@ Always_Included
validOutputsWithExtraContent: {
beta: [
`
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
Conditionally_Included
@ -904,14 +971,14 @@ Always_Included
],
flask: [
`
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask,beta,desktop)
///: BEGIN:ONLY_INCLUDE_IN(build-flask,build-beta,desktop)
Conditionally_Included
Conditionally_Included
@ -920,7 +987,7 @@ Conditionally_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
@ -930,10 +997,19 @@ Always_Included
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(desktop)
Conditionally_Included
Conditionally_Included
///: END:ONLY_INCLUDE_IN
Always_Included
Always_Included
Always_Included
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(build-flask)
Conditionally_Included
Conditionally_Included
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
Conditionally_Included
Conditionally_Included
@ -942,7 +1018,7 @@ Always_Included
Always_Included
Always_Included
`,
true,
false,
],
mmi: [
`

View File

@ -1,6 +1,6 @@
const path = require('path');
const semver = require('semver');
const { BuildType } = require('../lib/build-type');
const { loadBuildTypesConfig } = require('../lib/build-type');
const { BUILD_TARGETS, ENVIRONMENT } = require('./constants');
/**
@ -48,6 +48,8 @@ function getBrowserVersionMap(platforms, version) {
let buildType, buildVersionSummary, buildVersion;
if (prerelease) {
[buildType, buildVersionSummary] = prerelease;
// TODO(ritave): Figure out why the version 10.25.0-beta.1-flask.0 in the below comment is even possible
// since those are two different build types
// The version could be version: '10.25.0-beta.1-flask.0',
// That results in buildVersionSummary becomes 1-flask
// And we only want 1 from this string
@ -57,14 +59,7 @@ function getBrowserVersionMap(platforms, version) {
: buildVersionSummary;
if (!String(buildVersion).match(/^\d+$/u)) {
throw new Error(`Invalid prerelease build version: '${buildVersion}'`);
} else if (
![
BuildType.beta,
BuildType.flask,
BuildType.desktop,
BuildType.mmi,
].includes(buildType)
) {
} else if (!loadBuildTypesConfig().buildTypes[buildType].isPrerelease) {
throw new Error(`Invalid prerelease build type: ${buildType}`);
}
}

View File

@ -2,11 +2,13 @@
const concurrently = require('concurrently');
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const { BuildType } = require('./lib/build-type');
const { loadBuildTypesConfig } = require('./lib/build-type');
const stableBuildTypes = Object.values(BuildType).filter(
const buildTypesConfig = loadBuildTypesConfig();
const stableBuildTypes = Object.keys(buildTypesConfig.buildTypes).filter(
// Skip generating policy for MMI until that build has stabilized
(buildType) => buildType !== BuildType.mmi,
(buildType) => buildType !== 'mmi',
);
start().catch((error) => {
@ -24,7 +26,7 @@ async function start() {
yargsInstance
.option('build-types', {
alias: ['t'],
choices: Object.values(BuildType),
choices: Object.keys(buildTypesConfig.buildTypes),
default: stableBuildTypes,
demandOption: true,
description: 'The build type(s) to generate policy files for.',

6
development/lib/build-type.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import { Infer, Struct } from 'superstruct';
export type Unique<Element extends Struct<any>> = (
struct: Struct<Infer<Element>[], Infer<Element>>,
eq?: (a: Infer<Element>, b: Infer<Element>) => boolean,
) => Struct<Infer<Element>[], Infer<Element>>;

View File

@ -1,18 +1,169 @@
const fs = require('fs');
const { AssertionError } = require('assert');
const path = require('path');
const {
object,
string,
record,
optional,
array,
refine,
any,
boolean,
coerce,
union,
unknown,
validate,
nullable,
never,
} = require('superstruct');
const yaml = require('js-yaml');
const { uniqWith } = require('lodash');
const BUILDS_YML_PATH = path.resolve('./builds.yml');
/**
* The distribution this build is intended for.
*
* This should be kept in-sync with the `BuildType` map in `shared/constants/app.js`.
* @type {import('superstruct').Infer<typeof BuildTypesStruct> | null}
*/
const BuildType = {
beta: 'beta',
desktop: 'desktop',
flask: 'flask',
main: 'main',
mmi: 'mmi',
};
let cachedBuildTypes = null;
const BuildTypeInheritance = {
[BuildType.desktop]: [BuildType.flask],
};
/**
* Ensures that the array item contains only elements that are distinct from each other
*
* @template {Struct<any>} Element
* @type {import('./build-type').Unique<Element>}
*/
const unique = (struct, eq) =>
refine(struct, 'unique', (value) => {
if (uniqWith(value, eq).length === value.length) {
return true;
}
return 'Array contains duplicated values';
});
module.exports = { BuildType, BuildTypeInheritance };
const EnvDefinitionStruct = coerce(
object({ key: string(), value: unknown() }),
refine(record(string(), any()), 'Env variable declaration', (value) => {
if (Object.keys(value).length !== 1) {
return 'Declaration should have only one property, the name';
}
return true;
}),
(value) => ({ key: Object.keys(value)[0], value: Object.values(value)[0] }),
);
const EnvArrayStruct = unique(
array(union([string(), EnvDefinitionStruct])),
(a, b) => {
const keyA = typeof a === 'string' ? a : a.key;
const keyB = typeof b === 'string' ? b : b.key;
return keyA === keyB;
},
);
const BuildTypeStruct = object({
features: optional(unique(array(string()))),
env: optional(EnvArrayStruct),
isPrerelease: optional(boolean()),
manifestOverrides: optional(string()),
});
const CopyAssetStruct = object({ src: string(), dest: string() });
const ExclusiveIncludeAssetStruct = coerce(
object({ exclusiveInclude: string() }),
string(),
(exclusiveInclude) => ({ exclusiveInclude }),
);
const AssetStruct = union([CopyAssetStruct, ExclusiveIncludeAssetStruct]);
const FeatureStruct = object({
env: optional(EnvArrayStruct),
// TODO(ritave): Check if the paths exist
assets: optional(array(AssetStruct)),
});
const FeaturesStruct = refine(
record(
string(),
coerce(FeatureStruct, nullable(never()), () => ({})),
),
'feature definitions',
function* (value) {
let isValid = true;
const definitions = new Set();
for (const feature of Object.values(value)) {
for (const env of feature?.env ?? []) {
if (typeof env !== 'string') {
if (definitions.has(env.key)) {
isValid = false;
yield `Multiple defined features have a definition of "${env}" env variable, resulting in a conflict`;
}
definitions.add(env.key);
}
}
}
return isValid;
},
);
const BuildTypesStruct = refine(
object({
default: string(),
buildTypes: record(string(), BuildTypeStruct),
features: FeaturesStruct,
env: EnvArrayStruct,
}),
'BuildTypes',
(value) => {
if (!Object.keys(value.buildTypes).includes(value.default)) {
return `Default build type "${value.default}" does not exist in builds declarations`;
}
return true;
},
);
/**
* Loads definitions of build type and what they are composed of.
*
* @returns {import('superstruct').Infer<typeof BuildTypesStruct>}
*/
function loadBuildTypesConfig() {
if (cachedBuildTypes !== null) {
return cachedBuildTypes;
}
const buildsData = yaml.load(fs.readFileSync(BUILDS_YML_PATH, 'utf8'), {
json: true,
});
const [err, result] = validate(buildsData, BuildTypesStruct, {
coerce: true,
});
if (err !== undefined) {
throw new AssertionError({
message: constructFailureMessage(err),
});
}
cachedBuildTypes = result;
return buildsData;
}
/**
* Creates a user readable error message about parse failure.
*
* @param {import('superstruct').StructError} structError
* @returns {string}
*/
function constructFailureMessage(structError) {
return `Failed to parse builds.yml
-> ${structError
.failures()
.map(
(failure) =>
`${failure.message} (${BUILDS_YML_PATH}:.${failure.path.join('/')})`,
)
.join('\n -> ')}
`;
}
module.exports = { loadBuildTypesConfig };

View File

@ -1,5 +1,5 @@
const { version: manifestVersion } = require('../../package.json');
const { BuildType } = require('./build-type');
const { loadBuildTypesConfig } = require('./build-type');
/**
* Get the current version of the MetaMask extension. The base manifest version
@ -8,14 +8,14 @@ const { BuildType } = require('./build-type');
* The build version is needed because certain build types (such as beta) may
* be released multiple times during the release process.
*
* @param {BuildType} buildType - The build type.
* @param {string} buildType - The build type.
* @param {number} buildVersion - The build version.
* @returns {string} The MetaMask extension version.
*/
function getVersion(buildType, buildVersion) {
return buildType === BuildType.main || buildType === BuildType.beta
? manifestVersion
: `${manifestVersion}-${buildType}.${buildVersion}`;
return loadBuildTypesConfig().buildTypes[buildType].isPrerelease === true
? `${manifestVersion}-${buildType}.${buildVersion}`
: manifestVersion;
}
module.exports = { getVersion };

View File

@ -0,0 +1,114 @@
/* eslint-disable jsdoc/check-tag-names */
const assert = require('assert');
const DeclaredOnly = Symbol(
'This variable was declared only without being defined',
);
class Variables {
/**
* @type {Map<string, unknown | typeof DeclaredOnly>}
*/
#definitions = new Map();
/**
* @param {Iterable<string>} declarations
*/
constructor(declarations) {
for (const declaration of declarations) {
this.#definitions.set(declaration, DeclaredOnly);
}
}
/**
* @param {string} key - The name of the variable
* @throws {TypeError} If there is no definition of a variable.
*/
get(key) {
const value = this.getMaybe(key);
assert(
value !== DeclaredOnly,
new TypeError(
`Tried to access a declared, but not defined environmental variable "${key}"`,
),
);
return value;
}
/**
* Returns a declared, but maybe not defined variable.
*
* @param {string} key - The name of the variable
* @throws {TypeError} If there was no declaration of the variable.
* @returns The value, or undefined if the variables wasn't defined.
*/
getMaybe(key) {
assert(
this.isDeclared(key),
new TypeError(
`Tried to access an environmental variable "${key}" that wasn't declared in builds.yml`,
),
);
return this.#definitions.get(key);
}
/**
* Sets one key
*
* @overload
* @param {string} key
* @param {unknown} value
* @returns {void}
*/
/**
* @overload
* @param {Record<string, unknown>} records - Key-Value object
* @returns {void}
*/
/**
* @param {string | Record<string, unknown>} keyOrRecord
* @param {unknown} value
* @returns {void}
*/
set(keyOrRecord, value) {
if (typeof keyOrRecord === 'object') {
for (const [key, recordValue] of Object.entries(keyOrRecord)) {
this.set(key, recordValue);
}
return;
}
const key = keyOrRecord;
assert(
this.isDeclared(key),
`Tried to modify a variable "${key}" that wasn't declared in builds.yml`,
);
assert(value !== DeclaredOnly, `Tried to un-define "${key}" variable`);
this.#definitions.set(key, value);
}
isDeclared(key) {
return this.#definitions.has(key);
}
isDefined(key) {
return (
this.#definitions.has(key) && this.#definitions.get(key) !== DeclaredOnly
);
}
[Symbol.iterator] = this.declarations;
*declarations() {
yield* this.#definitions.keys();
}
*definitions() {
for (const [key, value] of this.#definitions.entries()) {
if (value !== DeclaredOnly) {
yield [key, value];
}
}
}
}
module.exports = { Variables };

View File

@ -5,7 +5,7 @@ const { hideBin } = require('yargs/helpers');
const { runCommand, runInShell } = require('./lib/run-command');
const { getVersion } = require('./lib/get-version');
const { BuildType } = require('./lib/build-type');
const { loadBuildTypesConfig } = require('./lib/build-type');
start().catch((error) => {
console.error(error);
@ -29,9 +29,9 @@ async function start() {
type: 'string',
})
.option('build-type', {
default: BuildType.main,
default: loadBuildTypesConfig().default,
description: 'The MetaMask extension build type',
choices: Object.values(BuildType),
choices: Object.keys(loadBuildTypesConfig().buildTypes),
})
.option('build-version', {
default: 0,
@ -86,7 +86,7 @@ async function start() {
}
const additionalUploadArgs = [];
if (buildType !== BuildType.main) {
if (buildType !== loadBuildTypesConfig().default) {
additionalUploadArgs.push('--dist-directory', `dist-${buildType}`);
}
// upload sentry source and sourcemaps

View File

@ -716,7 +716,7 @@
"@metamask/assets-controllers>@metamask/abi-utils": {
"packages": {
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
@ -725,10 +725,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/assets-controllers>abort-controller": {
@ -821,10 +821,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
@ -869,10 +869,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-provider": {
@ -1192,10 +1192,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/key-tree>@noble/ed25519": {
@ -1317,7 +1317,7 @@
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/rpc-methods>@metamask/browser-passworder": {
@ -1443,10 +1443,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
@ -2659,10 +2659,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"eth-ens-namehash": {
@ -2780,7 +2780,6 @@
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"browserify>process": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,

View File

@ -716,7 +716,7 @@
"@metamask/assets-controllers>@metamask/abi-utils": {
"packages": {
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
@ -725,10 +725,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/assets-controllers>abort-controller": {
@ -827,7 +827,6 @@
"@metamask/desktop>otpauth": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>process": true,
"browserify>stream-browserify": true,
"end-of-stream": true,
"extension-port-stream": true,
@ -893,10 +892,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
@ -941,10 +940,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-provider": {
@ -1264,10 +1263,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/key-tree>@noble/ed25519": {
@ -1414,10 +1413,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/post-message-stream>readable-stream": {
@ -1479,8 +1478,8 @@
"@metamask/snaps-ui": true,
"@metamask/snaps-utils": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true,
"eth-rpc-errors": true
"eth-rpc-errors": true,
"superstruct": true
}
},
"@metamask/rpc-methods>@metamask/browser-passworder": {
@ -1771,7 +1770,7 @@
"@metamask/snaps-ui": {
"packages": {
"@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/snaps-utils": {
@ -1792,15 +1791,15 @@
"@metamask/snaps-utils>rfdc": true,
"@metamask/snaps-utils>validate-npm-package-name": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>@metamask/snaps-registry": {
"packages": {
"@metamask/key-tree>@noble/secp256k1": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/snaps-utils>cron-parser": {
@ -1836,10 +1835,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
@ -3052,10 +3051,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"eth-ens-namehash": {
@ -3173,7 +3172,6 @@
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"browserify>process": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,

View File

@ -716,7 +716,7 @@
"@metamask/assets-controllers>@metamask/abi-utils": {
"packages": {
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
@ -725,10 +725,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/assets-controllers>abort-controller": {
@ -827,7 +827,6 @@
"@metamask/desktop>otpauth": true,
"browserify>buffer": true,
"browserify>events": true,
"browserify>process": true,
"browserify>stream-browserify": true,
"end-of-stream": true,
"extension-port-stream": true,
@ -893,10 +892,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
@ -941,10 +940,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-provider": {
@ -1264,10 +1263,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/key-tree>@noble/ed25519": {
@ -1414,10 +1413,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/post-message-stream>readable-stream": {
@ -1479,8 +1478,8 @@
"@metamask/snaps-ui": true,
"@metamask/snaps-utils": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true,
"eth-rpc-errors": true
"eth-rpc-errors": true,
"superstruct": true
}
},
"@metamask/rpc-methods>@metamask/browser-passworder": {
@ -1771,7 +1770,7 @@
"@metamask/snaps-ui": {
"packages": {
"@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/snaps-utils": {
@ -1792,15 +1791,15 @@
"@metamask/snaps-utils>rfdc": true,
"@metamask/snaps-utils>validate-npm-package-name": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/snaps-utils>@metamask/snaps-registry": {
"packages": {
"@metamask/key-tree>@noble/secp256k1": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/snaps-utils>cron-parser": {
@ -1836,10 +1835,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
@ -3052,10 +3051,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"eth-ens-namehash": {
@ -3173,7 +3172,6 @@
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"browserify>process": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,

View File

@ -716,7 +716,7 @@
"@metamask/assets-controllers>@metamask/abi-utils": {
"packages": {
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/assets-controllers>@metamask/abi-utils>@metamask/utils": {
@ -725,10 +725,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/assets-controllers>abort-controller": {
@ -821,10 +821,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-infura>eth-json-rpc-middleware": {
@ -869,10 +869,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/eth-json-rpc-provider": {
@ -1192,10 +1192,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/key-tree>@noble/ed25519": {
@ -1317,7 +1317,7 @@
"@metamask/key-tree": true,
"@metamask/key-tree>@noble/hashes": true,
"@metamask/utils": true,
"@metamask/utils>superstruct": true
"superstruct": true
}
},
"@metamask/rpc-methods>@metamask/browser-passworder": {
@ -1443,10 +1443,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"@metamask/utils>@ethereumjs/tx>@chainsafe/ssz": {
@ -2659,10 +2659,10 @@
"TextEncoder": true
},
"packages": {
"@metamask/utils>superstruct": true,
"browserify>buffer": true,
"nock>debug": true,
"semver": true
"semver": true,
"superstruct": true
}
},
"eth-ens-namehash": {
@ -2780,7 +2780,6 @@
"@ethersproject/abi": true,
"bn.js": true,
"browserify>buffer": true,
"browserify>process": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/common": true,
"eth-lattice-keyring>gridplus-sdk>@ethereumjs/tx": true,
"eth-lattice-keyring>gridplus-sdk>bech32": true,

View File

@ -531,6 +531,7 @@
"string.prototype.matchall": "^4.0.2",
"style-loader": "^0.21.0",
"stylelint": "^13.6.1",
"superstruct": "^1.0.3",
"terser": "^5.7.0",
"through2": "^4.0.2",
"ts-node": "^10.5.0",

View File

@ -1,4 +1,4 @@
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { DialogType } from '@metamask/rpc-methods';
///: END:ONLY_INCLUDE_IN
import { RestrictedMethods } from './permissions';
@ -20,18 +20,6 @@ export const ENVIRONMENT_TYPE_NOTIFICATION = 'notification';
export const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen';
export const ENVIRONMENT_TYPE_BACKGROUND = 'background';
/**
* The distribution this build is intended for.
*
* This should be kept in-sync with the `BuildType` map in `development/build/utils.js`.
*/
export const BuildType = {
beta: 'beta',
desktop: 'desktop',
flask: 'flask',
main: 'main',
} as const;
export const PLATFORM_BRAVE = 'Brave';
export const PLATFORM_CHROME = 'Chrome';
export const PLATFORM_EDGE = 'Edge';
@ -57,12 +45,12 @@ export const MESSAGE_TYPE = {
WALLET_REQUEST_PERMISSIONS: 'wallet_requestPermissions',
WATCH_ASSET: 'wallet_watchAsset',
WATCH_ASSET_LEGACY: 'metamask_watchAsset',
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
SNAP_DIALOG_ALERT: `${RestrictedMethods.snap_dialog}:alert`,
SNAP_DIALOG_CONFIRMATION: `${RestrictedMethods.snap_dialog}:confirmation`,
SNAP_DIALOG_PROMPT: `${RestrictedMethods.snap_dialog}:prompt`,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
MMI_AUTHENTICATE: 'metamaskinstitutional_authenticate',
MMI_REAUTHENTICATE: 'metamaskinstitutional_reauthenticate',
MMI_REFRESH_TOKEN: 'metamaskinstitutional_refresh_token',
@ -75,7 +63,7 @@ export const MESSAGE_TYPE = {
///: END:ONLY_INCLUDE_IN
} as const;
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
export const SNAP_DIALOG_TYPES = {
[DialogType.Alert]: MESSAGE_TYPE.SNAP_DIALOG_ALERT,
[DialogType.Confirmation]: MESSAGE_TYPE.SNAP_DIALOG_CONFIRMATION,

View File

@ -1,2 +0,0 @@
export const isMain = process.env.METAMASK_BUILD_TYPE === 'main';
export const isFlask = process.env.METAMASK_BUILD_TYPE === 'flask';

View File

@ -538,7 +538,7 @@ export enum MetaMetricsEventName {
OnboardingWalletVideoPlay = 'SRP Intro Video Played',
OnboardingTwitterClick = 'External Link Clicked',
ServiceWorkerRestarted = 'Service Worker Restarted',
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
UserClickedDeepLink = 'User clicked deeplink',
///: END:ONLY_INCLUDE_IN
}
@ -580,7 +580,7 @@ export enum MetaMetricsEventCategory {
Wallet = 'Wallet',
Desktop = 'Desktop',
ServiceWorkers = 'service_workers',
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
MMI = 'Institutional',
///: END:ONLY_INCLUDE_IN
}

View File

@ -4,7 +4,7 @@ export const CaveatTypes = Object.freeze({
export const RestrictedMethods = Object.freeze({
eth_accounts: 'eth_accounts',
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
snap_dialog: 'snap_dialog',
snap_notify: 'snap_notify',
snap_manageState: 'snap_manageState',
@ -16,7 +16,7 @@ export const RestrictedMethods = Object.freeze({
///: END:ONLY_INCLUDE_IN
} as const);
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
/**
* Exclude permissions by code fencing them to avoid any potential usage of excluded permissions at runtime. See: https://github.com/MetaMask/metamask-extension/pull/17321#pullrequestreview-1287014285.
* This is a fix for https://github.com/MetaMask/snaps-monorepo/issues/1103 and https://github.com/MetaMask/snaps-monorepo/issues/990.

View File

@ -1,4 +1,4 @@
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import type { SupportedCurve } from '@metamask/key-tree';
type SnapsMetadata = {

View File

@ -308,7 +308,7 @@ interface DappSuggestedGasFees {
* An object representing a transaction, in whatever state it is in.
*/
export interface TransactionMeta {
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
custodyStatus: string;
custodyId?: string;
///: END:ONLY_INCLUDE_IN

View File

@ -1,4 +1,4 @@
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
import browser from 'webextension-polyfill';
///: END:ONLY_INCLUDE_IN
import { memoize } from 'lodash';
@ -7,7 +7,7 @@ import {
fetchLocale,
loadRelativeTimeFormatLocaleData,
} from '../../ui/helpers/utils/i18n-helper';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
import { renderDesktopError } from '../../ui/pages/desktop-error/render-desktop-error';
import { EXTENSION_ERROR_PAGE_TYPES } from '../constants/desktop';
import { openCustomProtocol } from './deep-linking';
@ -44,7 +44,7 @@ export async function getErrorHtml(
errorKey,
supportLink,
metamaskState,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
err,
///: END:ONLY_INCLUDE_IN
) {
@ -65,7 +65,7 @@ export async function getErrorHtml(
const { currentLocaleMessages, enLocaleMessages } = response;
const t = getLocaleContext(currentLocaleMessages, enLocaleMessages);
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
const isDesktopEnabled = metamaskState?.desktopEnabled === true;
if (isDesktopEnabled) {
@ -120,7 +120,7 @@ export async function getErrorHtml(
`;
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
export const MMD_DOWNLOAD_LINK =
'https://github.com/MetaMask/metamask-desktop/releases';

View File

@ -1,14 +1,6 @@
let _supportLink = 'https://support.metamask.io';
///: BEGIN:ONLY_INCLUDE_IN(mmi)
_supportLink = 'https://mmi-support.zendesk.com/hc/en-us';
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(flask)
_supportLink = 'https://metamask-flask.zendesk.com/hc';
///: END:ONLY_INCLUDE_IN
export const SUPPORT_LINK = _supportLink;
// no destructuring as process.env detection stops working
// eslint-disable-next-line prefer-destructuring
export const SUPPORT_LINK = process.env.SUPPORT_LINK;
export const COINGECKO_LINK = 'https://www.coingecko.com/';
export const CRYPTOCOMPARE_LINK = 'https://www.cryptocompare.com/';

View File

@ -1 +1,2 @@
process.env.METAMASK_ENV = 'test';
process.env.METAMASK_ENVIRONMENT = 'test';
process.env.SUPPORT_LINK = 'https://support.metamask.io';

View File

@ -17,7 +17,7 @@ import SiteIcon from '../../ui/site-icon';
import UserPreferencedCurrencyDisplay from '../user-preferenced-currency-display';
import {
PRIMARY,
///: BEGIN:ONLY_INCLUDE_IN(beta,flask)
///: BEGIN:ONLY_INCLUDE_IN(build-beta,build-flask)
SUPPORT_REQUEST_LINK,
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/constants/common';
@ -27,7 +27,7 @@ import {
IMPORT_ACCOUNT_ROUTE,
CONNECT_HARDWARE_ROUTE,
DEFAULT_ROUTE,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
NOTIFICATIONS_ROUTE,
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/constants/routes';
@ -88,7 +88,7 @@ export default class AccountMenu extends Component {
toggleAccountMenu: PropTypes.func,
addressConnectedSubjectMap: PropTypes.object,
originOfCurrentTab: PropTypes.string,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount: PropTypes.number,
///: END:ONLY_INCLUDE_IN
};
@ -310,7 +310,7 @@ export default class AccountMenu extends Component {
toggleAccountMenu,
lockMetamask,
history,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
} = this.props;
@ -321,7 +321,7 @@ export default class AccountMenu extends Component {
let supportText = t('support');
let supportLink = SUPPORT_LINK;
///: BEGIN:ONLY_INCLUDE_IN(beta,flask)
///: BEGIN:ONLY_INCLUDE_IN(build-beta,build-flask)
supportText = t('needHelpSubmitTicket');
supportLink = SUPPORT_REQUEST_LINK;
///: END:ONLY_INCLUDE_IN
@ -415,7 +415,7 @@ export default class AccountMenu extends Component {
/>
<div className="account-menu__divider" />
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
<>
<AccountMenuItem
onClick={() => {

View File

@ -13,7 +13,7 @@ import {
getMetaMaskKeyrings,
getOriginOfCurrentTab,
getSelectedAddress,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
getUnreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
@ -31,7 +31,7 @@ function mapStateToProps(state) {
const accounts = getMetaMaskAccountsOrdered(state);
const origin = getOriginOfCurrentTab(state);
const selectedAddress = getSelectedAddress(state);
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const unreadNotificationsCount = getUnreadNotificationsCount(state);
///: END:ONLY_INCLUDE_IN
return {
@ -42,7 +42,7 @@ function mapStateToProps(state) {
keyrings: getMetaMaskKeyrings(state),
accounts,
shouldShowAccountsSearch: accounts.length >= SHOW_SEARCH_ACCOUNTS_MIN_COUNT,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
};

View File

@ -35,7 +35,7 @@ export default function KeyRingLabel({ keyring }) {
label = null;
}
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
if (type.startsWith('Custody') && /JSONRPC/u.test(type)) {
label = type.split(' - ')[1];
return null;

View File

@ -34,15 +34,15 @@
@import 'edit-gas-fee-popover/network-statistics/status-slider/index';
@import 'edit-gas-fee-popover/edit-gas-tooltip/index';
@import 'flask/experimental-area/index';
@import 'flask/snap-content-footer/index';
@import 'flask/snap-install-warning/index';
@import 'flask/snap-remove-warning/index';
@import 'flask/snap-ui-renderer/index';
@import 'flask/snap-ui-markdown/index';
@import 'flask/snap-delineator/index';
@import 'flask/snap-settings-card/index';
@import 'flask/update-snap-permission-list/index';
@import 'flask/copyable/index';
@import 'snaps/snap-content-footer/index';
@import 'snaps/snap-install-warning/index';
@import 'snaps/snap-remove-warning/index';
@import 'snaps/snap-ui-renderer/index';
@import 'snaps/snap-ui-markdown/index';
@import 'snaps/snap-delineator/index';
@import 'snaps/snap-settings-card/index';
@import 'snaps/update-snap-permission-list/index';
@import 'snaps/copyable/index';
@import 'gas-details-item/index';
@import 'gas-details-item/gas-details-item-title/index';
@import 'gas-timing/index';

View File

@ -10,9 +10,9 @@ import {
} from '../../../../shared/constants/metametrics';
import NetworkDisplay from '../network-display';
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
import BetaHeader from '../beta-header';
///: END:ONLY_INCLUDE_IN(beta)
///: END:ONLY_INCLUDE_IN
export default class AppHeader extends PureComponent {
static propTypes = {
@ -27,11 +27,13 @@ export default class AppHeader extends PureComponent {
disabled: PropTypes.bool,
disableNetworkIndicator: PropTypes.bool,
isAccountMenuOpen: PropTypes.bool,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount: PropTypes.number,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
desktopEnabled: PropTypes.bool,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
showBetaHeader: PropTypes.bool,
///: END:ONLY_INCLUDE_IN
onClick: PropTypes.func,
@ -77,7 +79,7 @@ export default class AppHeader extends PureComponent {
selectedAddress,
disabled,
isAccountMenuOpen,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
} = this.props;
@ -104,7 +106,7 @@ export default class AppHeader extends PureComponent {
>
<Identicon address={selectedAddress} diameter={32} addBorder />
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount > 0 && (
<div className="account-menu__icon__notification-count">
{unreadNotificationsCount}
@ -124,10 +126,10 @@ export default class AppHeader extends PureComponent {
disableNetworkIndicator,
disabled,
onClick,
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
showBetaHeader,
///: END:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
desktopEnabled,
///: END:ONLY_INCLUDE_IN
} = this.props;
@ -135,9 +137,9 @@ export default class AppHeader extends PureComponent {
return (
<>
{
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
showBetaHeader ? <BetaHeader /> : null
///: END:ONLY_INCLUDE_IN(beta)
///: END:ONLY_INCLUDE_IN
}
<div className="app-header">
@ -152,7 +154,7 @@ export default class AppHeader extends PureComponent {
}}
/>
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
desktopEnabled && process.env.METAMASK_DEBUG && (
<div data-testid="app-header-desktop-dev-logo">
<MetaFoxLogo

View File

@ -2,10 +2,10 @@ import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { compose } from 'redux';
import {
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
getUnreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
getShowBetaHeader,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
@ -20,16 +20,16 @@ const mapStateToProps = (state) => {
selectedAddress,
isUnlocked,
isAccountMenuOpen,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(desktop)
desktopEnabled,
///: END:ONLY_INCLUDE_IN
} = metamask;
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const unreadNotificationsCount = getUnreadNotificationsCount(state);
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
const showBetaHeader = getShowBetaHeader(state);
///: END:ONLY_INCLUDE_IN
@ -38,11 +38,13 @@ const mapStateToProps = (state) => {
selectedAddress,
isUnlocked,
isAccountMenuOpen,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
unreadNotificationsCount,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(desktop)
desktopEnabled,
///: END:ONLY_INCLUDE_IN
///: BEGIN:ONLY_INCLUDE_IN(beta)
///: BEGIN:ONLY_INCLUDE_IN(build-beta)
showBetaHeader,
///: END:ONLY_INCLUDE_IN
};

View File

@ -24,7 +24,7 @@ export default class ConfirmPageContainerContent extends Component {
dataComponent: PropTypes.node,
dataHexComponent: PropTypes.node,
detailsComponent: PropTypes.node,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
insightComponent: PropTypes.node,
///: END:ONLY_INCLUDE_IN
errorKey: PropTypes.string,
@ -59,7 +59,7 @@ export default class ConfirmPageContainerContent extends Component {
renderContent() {
const { detailsComponent, dataComponent } = this.props;
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const { insightComponent } = this.props;
if (insightComponent && (detailsComponent || dataComponent)) {
@ -73,7 +73,7 @@ export default class ConfirmPageContainerContent extends Component {
return (
detailsComponent ||
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
insightComponent ||
///: END:ONLY_INCLUDE_IN
dataComponent
@ -86,7 +86,7 @@ export default class ConfirmPageContainerContent extends Component {
detailsComponent,
dataComponent,
dataHexComponent,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
insightComponent,
///: END:ONLY_INCLUDE_IN
} = this.props;
@ -120,7 +120,7 @@ export default class ConfirmPageContainerContent extends Component {
)}
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
insightComponent
///: END:ONLY_INCLUDE_IN
}

View File

@ -30,9 +30,9 @@ import NetworkAccountBalanceHeader from '../network-account-balance-header/netwo
import { fetchTokenBalance } from '../../../../shared/lib/token-util.ts';
import SetApproveForAllWarning from '../set-approval-for-all-warning';
import { useI18nContext } from '../../../hooks/useI18nContext';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import useTransactionInsights from '../../../hooks/useTransactionInsights';
///: END:ONLY_INCLUDE_IN(flask)
///: END:ONLY_INCLUDE_IN
import {
getAccountName,
getAddressBookEntry,
@ -140,7 +140,7 @@ const ConfirmPageContainer = (props) => {
setCollectionBalance(tokenBalance?.balance?.words?.[0] || 0);
}, [fromAddress, tokenAddress]);
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
// As confirm-transction-base is converted to functional component
// this code can bemoved to it.
const insightComponent = useTransactionInsights({
@ -206,7 +206,7 @@ const ConfirmPageContainer = (props) => {
detailsComponent={detailsComponent}
dataComponent={dataComponent}
dataHexComponent={dataHexComponent}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
insightComponent={insightComponent}
///: END:ONLY_INCLUDE_IN
errorMessage={errorMessage}

View File

@ -7,6 +7,6 @@ export {
default as ConfirmPageContainerContent,
ConfirmPageContainerSummary,
} from './confirm-page-container-content';
///: BEGIN:ONLY_INCLUDE_IN(flask)
export { SnapInsight } from './flask/snap-insight';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
export { SnapInsight } from './snaps/snap-insight';
///: END:ONLY_INCLUDE_IN

View File

@ -2,8 +2,8 @@
@import 'confirm-page-container-header/index';
@import 'confirm-detail-row/index';
@import 'confirm-page-container-navigation/index';
///: BEGIN:ONLY_INCLUDE_IN(flask)
@import 'flask/index';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
@import 'snaps/index';
///: END:ONLY_INCLUDE_IN
.confirm-page-container {

View File

@ -13,13 +13,13 @@ import {
TextVariant,
} from '../../../../helpers/constants/design-system';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { useTransactionInsightSnap } from '../../../../hooks/flask/useTransactionInsightSnap';
import { useTransactionInsightSnap } from '../../../../hooks/snaps/useTransactionInsightSnap';
import Box from '../../../ui/box/box';
import { SnapUIRenderer } from '../../flask/snap-ui-renderer';
import { SnapDelineator } from '../../flask/snap-delineator';
import { SnapUIRenderer } from '../../snaps/snap-ui-renderer';
import { SnapDelineator } from '../../snaps/snap-delineator';
import { DelineatorType } from '../../../../helpers/constants/flask';
import { getSnapName } from '../../../../helpers/utils/util';
import { Copyable } from '../../flask/copyable';
import { Copyable } from '../../snaps/copyable';
import { getTargetSubjectMetadata } from '../../../../selectors';
export const SnapInsight = ({ transaction, origin, chainId, selectedSnap }) => {

View File

@ -12,11 +12,11 @@ import TextField from '../../ui/text-field';
import ConfirmationNetworkSwitch from '../../../pages/confirmation/components/confirmation-network-switch';
import UrlIcon from '../../ui/url-icon';
import Tooltip from '../../ui/tooltip/tooltip';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import { SnapDelineator } from '../flask/snap-delineator';
import { Copyable } from '../flask/copyable';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { SnapDelineator } from '../snaps/snap-delineator';
import { Copyable } from '../snaps/copyable';
import Spinner from '../../ui/spinner';
import { SnapUIMarkdown } from '../flask/snap-ui-markdown';
import { SnapUIMarkdown } from '../snaps/snap-ui-markdown';
///: END:ONLY_INCLUDE_IN
export const safeComponentList = {
@ -40,7 +40,7 @@ export const safeComponentList = {
TruncatedDefinitionList,
Typography,
UrlIcon,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
SnapDelineator,
Copyable,
Spinner,

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { isEqual } from 'lodash';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { isObject } from '@metamask/utils';
import {
SnapCaveatType,
@ -11,7 +11,7 @@ import {
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
import { PageContainerFooter } from '../../ui/page-container';
import PermissionsConnectFooter from '../permissions-connect-footer';
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import { RestrictedMethods } from '../../../../shared/constants/permissions';
///: END:ONLY_INCLUDE_IN
import { PermissionPageContainerContent } from '.';
@ -22,7 +22,7 @@ export default class PermissionPageContainer extends Component {
rejectPermissionsRequest: PropTypes.func.isRequired,
selectedIdentities: PropTypes.array,
allIdentitiesSelected: PropTypes.bool,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
currentPermissions: PropTypes.object,
///: END:ONLY_INCLUDE_IN
request: PropTypes.object,
@ -41,7 +41,7 @@ export default class PermissionPageContainer extends Component {
requestMetadata: {},
selectedIdentities: [],
allIdentitiesSelected: false,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
currentPermissions: {},
///: END:ONLY_INCLUDE_IN
};
@ -70,7 +70,7 @@ export default class PermissionPageContainer extends Component {
getRequestedMethodState(methodNames) {
return methodNames.reduce((acc, methodName) => {
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
if (methodName === RestrictedMethods.wallet_snap) {
acc[methodName] = this.getDedupedSnapPermissions();
return acc;
@ -81,7 +81,7 @@ export default class PermissionPageContainer extends Component {
}, {});
}
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
getDedupedSnapPermissions() {
const permission =
this.props.request.permissions[WALLET_SNAP_PERMISSION_KEY];

View File

@ -1,7 +1,7 @@
import { connect } from 'react-redux';
import {
getMetaMaskIdentities,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
getPermissions,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
@ -9,7 +9,7 @@ import PermissionPageContainer from './permission-page-container.component';
const mapStateToProps = (state, ownProps) => {
const { selectedIdentities } = ownProps;
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
const currentPermissions = getPermissions(
state,
ownProps.request.metadata?.origin,
@ -22,7 +22,7 @@ const mapStateToProps = (state, ownProps) => {
return {
allIdentitiesSelected,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
currentPermissions,
///: END:ONLY_INCLUDE_IN
};

View File

@ -7,12 +7,12 @@ import {
FLEX_DIRECTION,
JustifyContent,
} from '../../../helpers/constants/design-system';
///: BEGIN:ONLY_INCLUDE_IN(flask)
import SnapAuthorship from '../flask/snap-authorship';
///: BEGIN:ONLY_INCLUDE_IN(snaps)
import SnapAuthorship from '../snaps/snap-authorship';
///: END:ONLY_INCLUDE_IN
export default class PermissionsConnectHeader extends Component {
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
static contextTypes = {
t: PropTypes.func,
};
@ -28,7 +28,7 @@ export default class PermissionsConnectHeader extends Component {
headerText: PropTypes.string,
leftIcon: PropTypes.node,
rightIcon: PropTypes.node,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
snapVersion: PropTypes.string,
isSnapInstallOrUpdate: PropTypes.bool,
///: END:ONLY_INCLUDE_IN
@ -48,12 +48,12 @@ export default class PermissionsConnectHeader extends Component {
siteOrigin,
leftIcon,
rightIcon,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
isSnapInstallOrUpdate,
///: END:ONLY_INCLUDE_IN
} = this.props;
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
if (isSnapInstallOrUpdate) {
return null;
}
@ -80,7 +80,7 @@ export default class PermissionsConnectHeader extends Component {
className,
headerTitle,
headerText,
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
siteOrigin,
isSnapInstallOrUpdate,
///: END:ONLY_INCLUDE_IN
@ -95,7 +95,7 @@ export default class PermissionsConnectHeader extends Component {
{this.renderHeaderIcon()}
<div className="permissions-connect-header__title">{headerTitle}</div>
{
///: BEGIN:ONLY_INCLUDE_IN(flask)
///: BEGIN:ONLY_INCLUDE_IN(snaps)
isSnapInstallOrUpdate && <SnapAuthorship snapId={siteOrigin} />
///: END:ONLY_INCLUDE_IN
}

View File

@ -8,7 +8,7 @@ import { MESSAGE_TYPE } from '../../../../shared/constants/app';
import {
getURLHostName,
sanitizeString,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
shortenAddress,
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/utils/util';
@ -23,7 +23,7 @@ import {
FONT_WEIGHT,
TEXT_ALIGN,
TextColor,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
IconColor,
DISPLAY,
BLOCK_SIZES,
@ -40,7 +40,7 @@ import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banne
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
import { getValueFromWeiHex } from '../../../../shared/modules/conversion.utils';
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { Icon, IconName, Text } from '../../component-library';
import Box from '../../ui/box/box';
///: END:ONLY_INCLUDE_IN
@ -73,7 +73,7 @@ export default class SignatureRequestOriginal extends Component {
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
cancelAll: PropTypes.func.isRequired,
provider: PropTypes.object,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
selectedAccount: PropTypes.object,
///: END:ONLY_INCLUDE_IN
};
@ -185,7 +185,7 @@ export default class SignatureRequestOriginal extends Component {
) : null}
{
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.props.selectedAccount.address ===
this.props.fromAccount.address ? null : (
<Box

View File

@ -13,7 +13,7 @@ import {
getTotalUnapprovedMessagesCount,
getPreferences,
getCurrentCurrency,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getSelectedAccount,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
@ -56,7 +56,7 @@ function mapStateToProps(state, ownProps) {
messagesList,
messagesCount,
provider,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
selectedAccount: getSelectedAccount(state),
///: END:ONLY_INCLUDE_IN
};

View File

@ -5,7 +5,7 @@ import LedgerInstructionField from '../ledger-instruction-field';
import {
sanitizeMessage,
getURLHostName,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
shortenAddress,
///: END:ONLY_INCLUDE_IN
} from '../../../helpers/utils/util';
@ -19,7 +19,7 @@ import {
FONT_WEIGHT,
TEXT_ALIGN,
TextColor,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
IconColor,
DISPLAY,
BLOCK_SIZES,
@ -36,7 +36,7 @@ import SecurityProviderBannerMessage from '../security-provider-banner-message/s
import { SECURITY_PROVIDER_MESSAGE_SEVERITIES } from '../security-provider-banner-message/security-provider-banner-message.constants';
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
import { getValueFromWeiHex } from '../../../../shared/modules/conversion.utils';
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
import { Icon, IconName, Text } from '../../component-library';
import Box from '../../ui/box/box';
///: END:ONLY_INCLUDE_IN
@ -93,7 +93,7 @@ export default class SignatureRequest extends PureComponent {
mostRecentOverviewPage: PropTypes.string,
showRejectTransactionsConfirmationModal: PropTypes.func.isRequired,
cancelAll: PropTypes.func.isRequired,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
// Used to show a warning if the signing account is not the selected account
// Largely relevant for contract wallet custodians
selectedAccount: PropTypes.object,
@ -280,7 +280,7 @@ export default class SignatureRequest extends PureComponent {
) : null}
{
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
this.props.selectedAccount.address === address ? null : (
<Box
className="request-signature__mismatch-info"

View File

@ -10,7 +10,7 @@ import {
getCurrentCurrency,
getPreferences,
conversionRateSelector,
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
getSelectedAccount,
///: END:ONLY_INCLUDE_IN
} from '../../../selectors';
@ -58,7 +58,7 @@ function mapStateToProps(state, ownProps) {
subjectMetadata: getSubjectMetadata(state),
// not forwarded to component
allAccounts: accountsWithSendEtherInfoSelector(state),
///: BEGIN:ONLY_INCLUDE_IN(mmi)
///: BEGIN:ONLY_INCLUDE_IN(build-mmi)
selectedAccount: getSelectedAccount(state),
///: END:ONLY_INCLUDE_IN
};

Some files were not shown because too many files have changed in this diff Show More