-
Notifications
You must be signed in to change notification settings - Fork 5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(27255): allow local modification for remote feature flags #29696
base: main
Are you sure you want to change the base?
Conversation
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
@metamaskbot update-policies |
No dependency changes detected. Learn more about Socket for GitHub ↗︎ 👍 No dependency changes detected in pull request |
Policy update failed. You can review the logs or retry the policy update here |
1e9217f
to
1905574
Compare
@metamaskbot update-policies |
Policy update failed. You can review the logs or retry the policy update here |
1905574
to
ced7bf4
Compare
@metamaskbot update-policies |
Policies updated. 🧠 Learn how: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff |
704d4ab
to
511c2d5
Compare
@metamaskbot update-policies |
Policies updated. 🧠 Learn how: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff |
6ccfac6
to
804e3d7
Compare
@metamaskbot update-policies |
Builds ready [804e3d7]
Page Load Metrics (1723 ± 60 ms)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Policies updated. 🧠 Learn how: https://lavamoat.github.io/guides/policy-diff/#what-to-look-for-when-reviewing-a-policy-diff |
Builds ready [16c667e]
Page Load Metrics (1860 ± 60 ms)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Walk through the PR changes from here - |
/** | ||
* Feature flags to control business logic behavior | ||
*/ | ||
remoteFeatureFlags?: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Include the type of
remoteFeatureFlags
in manifest as optional value. We includetestFlagForThreshold
that's created for testing purpose in remote feature flag API .
development/build/manifest.js
Outdated
@@ -11,6 +11,7 @@ const baradDurManifest = isManifestV3 | |||
? require('../../app/manifest/v3/_barad_dur.json') | |||
: require('../../app/manifest/v2/_barad_dur.json'); | |||
const { loadBuildTypesConfig } = require('../lib/build-type'); | |||
const manifestFlags = require('../../manifest-flags.json'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Read customized manifestFlags from
manifest-flags.json
so we can override the one from controller (API) - old build system
@@ -1,3 +1,5 @@ | |||
import manifestFlags from '../../../../../manifest-flags.json'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Read customized manifestFlags from
manifest-flags.json
so we can override the one from controller (API) - webpack build
development/build/manifest.js
Outdated
@@ -47,8 +48,10 @@ function createManifestTasks({ | |||
browserVersionMap[platform], | |||
await getBuildModifications(buildType, platform), | |||
customArrayMerge, | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Add customized local
manifestFlags
to manifest file (old build system)
…anifest-flags.json file sync way in webpack build
Builds ready [77ad1e5]
Page Load Metrics (2081 ± 87 ms)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
Builds ready [b09f6a5]
Page Load Metrics (1884 ± 76 ms)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
// Mock fs.readFileSync | ||
const fs = require('fs'); | ||
const originalReadFileSync = fs.readFileSync; | ||
fs.readFileSync = () => JSON.stringify(mockFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT:
Node's testing library has mocking built it. I think the right way to do this is to create a before
block within this new 'manifest flags in development mode'
describe
block:
// (note: this is untested)
const originalReadFileSync = fs.readFileSync;
before("mock `.manifest-flags.json`", () => {
mock.method(fs, 'readFileSync', (path: string, options: object) => {
// only override reads for `.manifest-flags.json`:
if (path === resolve(__dirname, <the path to the file here>)) {
// mock `.manifest-flags.json`
return JSON.stringify(mockFlags);
}
return originalReadFileSync(path, options);
});
});
after(() => mock.restoreAll());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump
} | ||
}); | ||
|
||
it('handles missing manifest flags file', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
love that you are testing the error conditions! can you also add a test for the case where the fs.readFileSync
error
is NOT ENOENT
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump
Builds ready [51b6de6]
Page Load Metrics (1598 ± 72 ms)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
…d from .dist; refactor fs to stdlib modules
…roperty 'definitions' of '(intermediate value)' as it is undefined."
Builds ready [3a53d83]
Page Load Metrics (1628 ± 115 ms)
Bundle size diffs [🚨 Warning! Bundle size has increased!]
|
// Mock fs.readFileSync | ||
const fs = require('fs'); | ||
const originalReadFileSync = fs.readFileSync; | ||
fs.readFileSync = () => JSON.stringify(mockFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump
} | ||
}); | ||
|
||
it('handles missing manifest flags file', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump
const manifestFlags = getManifestFlags().remoteFeatureFlags; | ||
const stateFlags = state.metamask.remoteFeatureFlags; | ||
|
||
return merge({}, stateFlags, manifestFlags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason merge
makes the properties types of manifestFlags
required, even though they are not.
FeatureFlags & {
testFlagForThreshold: {
name: string;
value: string;
};
}
So I think the return type of this function is incorrect, as I think testFlagForThreshold
should be optional? Does that sound right?
@@ -160,7 +191,7 @@ function createManifestTasks({ | |||
|
|||
// helper for reading and deserializing json from fs | |||
async function readJson(file) { | |||
return JSON.parse(await fs.readFile(file, 'utf8')); | |||
return JSON.parse(await readFileSync(file, 'utf8')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function doesn't need to be async if we are reading the file synchronously. ORRRRR we don't need to read it synchronously if the function can just be async.
Anyway you want to do it is fine. I just need to be consistent (I don't know why our lint rules didn't catch it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think though your suggestion of await readFileSync
is incorrect though 🙂
const { definitions } = await fromIniFile( | ||
path.resolve(__dirname, '..', '..', '.metamaskrc'), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since our build reads from both .metamaskrc
and a .metamaskprodrc
should we do that here, too?
testFlagForThreshold: { | ||
name: string; | ||
value: string; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bump
<div className="settings-page__content-description"> | ||
The remote feature flags here by <b>getRemoteFeatureFlags()</b> is | ||
retrieved from one of the following sources: | ||
<br /> | ||
1) manifest-flags.json file 2) RemoteFeatureFlagsController | ||
<br /> | ||
Modify the manifest-flags.json file will change the state locally. | ||
</div> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its nice to be able to see this in the UI like this, but I think the section is in need of some UI polish, as well as some improvement on the copy itself. Maybe we could get someone from a design team in here to make some improvments?
It also doesn't seem to be something that should be at the top of this settings page :-/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have to block on this. I think we can merge and then improve in a later PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a major problem. If you put in .manifest-overrides.json the following:
{
"_flags": {
"remoteFeatureFlags": { "testBooleanFlag": false }
}
}
Then the built manifest.json has:
"_flags": {
"_flags": {
"remoteFeatureFlags": {
"testBooleanFlag": false
}
}
}
But you don't want to just remove the _flags
level in .manifest-overrides.json, because you want any manifest property to be changeable, with a config like this:
{
"_flags": {
"remoteFeatureFlags": { "testBooleanFlag": false }
},
"version": "1.1.1",
"version_name": "1.1.1"
}
So you need to make these two changes, one for the webpack build and one for the browserify build. I have tested these changes in many different configurations myself.
@@ -47,8 +77,9 @@ function createManifestTasks({ | |||
browserVersionMap[platform], | |||
await getBuildModifications(buildType, platform), | |||
customArrayMerge, | |||
// Only include _flags if manifestFlags has content | |||
manifestFlags && { _flags: manifestFlags }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manifestFlags && { _flags: manifestFlags }, | |
manifestFlags, |
} | ||
|
||
if (manifestFlags) { | ||
browserManifest._flags = manifestFlags; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
browserManifest._flags = manifestFlags; | |
browserManifest = merge(browserManifest, manifestFlags); |
Also needs at the top of this file:
import { merge } from 'lodash';
Description
Note
You can now follow the steps here to review the PR.
Local Feature Flag Override System
This PR implements a system that allows developers to override remote feature flags locally through
manifest.json
, providing more flexibility in development and testing environments.Key Features
Local Feature Flag Override
remoteFeatureFlag
values by defining them inmanifest.json
Testing Integration
remoteFeatureFlag
objects to be passed within testwithFixtures
Developer Validation
This enhancement streamlines the development workflow by providing local control over feature flags without requiring changes to the controller or deployment configurations.
Usage Example
A. Local build
remoteFeatureFlags
inmanifest.json
:B. e2e test
Add the customized value in
Related issues
Fixes: #27255
Manual testing steps
Please check <### Usage Example> above to check how to test manually.
Screenshots/Recordings
Before
After
Pre-merge author checklist
Pre-merge reviewer checklist