Skip to content
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

Add build-time code exclusion using code fencing #12060

Merged
merged 30 commits into from
Sep 14, 2021
Merged

Conversation

rekmarks
Copy link
Member

@rekmarks rekmarks commented Sep 10, 2021

This PR adds build-time code exclusion by means of code fencing. For details, please see the README. Note that linting of transformed files as a form of validation is added in a follow-up, #12075.

Hopefully exhaustive tests are added to ensure that the transform works according to its specification. Since these tests are Node-only, they required their own Jest config. The recommended way to work with multiple Jest configs is using the projects field in the Jest config, however that feature breaks coverage collection. That being the case, I had to set up two separate Jest configs. In order to get both test suites to run in parallel, Jest is now invoked via a script, ./test/run-jest.sh.

By way of example, this build system feature allows us to add fences like this:

this.store.updateStructure({
  ...,
  GasFeeController: this.gasFeeController,
  TokenListController: this.tokenListController,
  ///: BEGIN:ONLY_INCLUDE_IN(beta)
  PluginController: this.pluginController,
  ///: END:ONLY_INCLUDE_IN
});

Which at build time are transformed to the following if the build type is not beta:

this.store.updateStructure({
  ...,
  GasFeeController: this.gasFeeController,
  TokenListController: this.tokenListController,
});

Copy link
Member

@kumavis kumavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the example luke 🧙‍♂️

development/build/transforms/code-fencing.js Outdated Show resolved Hide resolved
@kumavis
Copy link
Member

kumavis commented Sep 10, 2021

generally good

@metamaskbot
Copy link
Collaborator

Builds ready [6d9512e]
Page Load Metrics (371 ± 18 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint5450833510048
domContentLoaded3264943563718
load3425113713718
domInteractive3264943563718

@metamaskbot
Copy link
Collaborator

Builds ready [6f97fa6]
Page Load Metrics (381 ± 20 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint514313379646
domContentLoaded3294923633818
load3455183814120
domInteractive3294913633818

@metamaskbot
Copy link
Collaborator

Builds ready [ba99fcd]
Page Load Metrics (379 ± 20 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint4651328215172
domContentLoaded3275023654220
load3415113794120
domInteractive3275023654220

@rekmarks rekmarks force-pushed the code-fencing branch 2 times, most recently from bd95c35 to 7f2e40a Compare September 11, 2021 08:23
@metamaskbot
Copy link
Collaborator

Builds ready [7f2e40a]
Page Load Metrics (416 ± 28 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint6049534612460
domContentLoaded3385954005828
load3536034165828
domInteractive3385954005828

@metamaskbot
Copy link
Collaborator

Builds ready [f74378d]
Page Load Metrics (404 ± 21 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint5550636410952
domContentLoaded3374943864019
load3525304044321
domInteractive3374943864019

@metamaskbot
Copy link
Collaborator

Builds ready [b0c42ec]
Page Load Metrics (361 ± 18 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint484933407737
domContentLoaded3204793453617
load3344883613718
domInteractive3204793453617

darkwing
darkwing previously approved these changes Sep 13, 2021
Copy link
Contributor

@darkwing darkwing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good and my testing was successful. Well done, and very quickly at that!

'<rootDir>/shared/**/?(*.)+(test).js',
],
// TODO: enable resetMocks
// resetMocks: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this take place in a later update?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resetMocks was already not enabled, and enabling it caused errors. I'm just highlighting that we should enable it here!

kumavis
kumavis previously approved these changes Sep 13, 2021
Copy link
Member

@kumavis kumavis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm 🔥

development/build/transforms/remove-fenced-code.js Outdated Show resolved Hide resolved
setupFiles: ['<rootDir>/test/setup.js', '<rootDir>/test/env.js'],
setupFilesAfterEnv: ['<rootDir>/test/jest/setup.js'],
testMatch: ['<rootDir>/ui/**/*.test.js', '<rootDir>/shared/**/*.test.js'],
testTimeout: 2500,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks new - is this the default setting?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We bumped the timeout at one point in the module template: https://github.com/MetaMask/metamask-module-template/blob/main/jest.config.js#L26

test/run-jest.sh Outdated Show resolved Hide resolved
test/run-jest.sh Outdated
# For storing PIDs of subshells
PIDS=(0 0)

yarn 'jest' '--config=./jest.config.js' "$@" & PIDS[0]=$!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern seems questionable 🤔 This means if one process fails, the other will keep going and there will be no way to stop it. Sounds like a pain.

We have a package that is supposed to do this sort of thing for us: concurrently. I don't think it works correctly in all cases, but it might work more often than this.

I think we could scrap this whole script and call concurrently directly from the npm script.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means if one process fails, the other will keep going and there will be no way to stop it. Sounds like a pain.

This is an intentional feature, not a bug! The idea being that, in CI, you want to know of all unit test failures, not just the ones for a particular config. This is an attempt to replicate what the Jest projects feature does, which is to run all tests for every project and then print the complete results.

If developing locally, the tests for a particular config can be run by doing yarn jest --config=PATH_TO_CONFIG.

On the topic of using concurrently, that seems like a good idea, since it appears to be a well-maintained tool that does exactly what I'm trying to do. We may still have to keep this script in order to do something like yarn test:unit:jest --watch and pass the --watch argument to both invocations of jest. That's what the "$@" is about in the Bash script.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to be clear, when I say "there is no way to stop it", what I mean is that the interrupt signal no longer halts the script. I certainly hope disabling CTRL+C wasn't intentional! It's incredibly frustrating when scripts can't be halted.

Copy link
Member

@Gudahtt Gudahtt Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... I tested this, and it works 🤔 I guess it's because these tests don't spawn child processes? This was a struggle to get working with the e2e tests. Our attempts to combine tests just with bash resulted in them continuing on after interrupting the main process.

Copy link
Member Author

@rekmarks rekmarks Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to go with concurrently but keep the bash script. I went for concurrently because a tool we're already using is probably better than a weird Bash loop-and-wait implementation. However, I invoke concurrently in the .sh file to preserve the ability to pass arguments to both jest processes by doing e.g. yarn test:unit:jest --silent.

662f5ae

@metamaskbot
Copy link
Collaborator

Builds ready [0828289]
Page Load Metrics (375 ± 17 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint5343630312560
domContentLoaded3244733603517
load3394813753617
domInteractive3244733603517

@metamaskbot
Copy link
Collaborator

Builds ready [073f7cf]
Page Load Metrics (371 ± 10 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint534153349546
domContentLoaded331398355178
load3474343712010
domInteractive331398355178

@rekmarks
Copy link
Member Author

rekmarks commented Sep 14, 2021

892a0a1 fixes a RegEx-related bug introduced in ce15bf2, and a completely different bug discovered while trying to fix the RegEx bug.

The RegEx bug was due to (what seems to me) somewhat strange behavior by the JavaScript RegEx engine (or just the spec, who knows), in that /s*\w+/gmu will match '\nstuff\n' as the single line '\nstuff', as opposed the two lines '' and stuff that we might expect. The RegEx had to be modified to avoid this and still produce the desired effect.

While writing tests to ensure that the parser indeed ignores sentinels preceded by non-whitespace characters (i.e. what ce15bf2 was supposed to accomplish), I discovered that the multiSplice function didn't handle the case where splicingIndices.length === 2. Specifically, it would insert the last retained part of the original string twice. This has now been fixed.

@metamaskbot
Copy link
Collaborator

Builds ready [892a0a1]
Page Load Metrics (378 ± 24 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint454853369747
domContentLoaded3315113635125
load3455133785024
domInteractive3315113635125

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@brad-decker brad-decker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. Good work. Tested locally and it checks out.

@@ -0,0 +1,123 @@
# Local Browserify Transforms
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some incredible documentation by the way. Not just this but the inline docs too.

I was expecting this transform to be difficult to understand, but it was really straightforward with all of these explanations at each step.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1000

@rekmarks rekmarks merged commit 3de3765 into develop Sep 14, 2021
@rekmarks rekmarks deleted the code-fencing branch September 14, 2021 17:00
@github-actions github-actions bot locked and limited conversation to collaborators Sep 14, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants