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

feat: install npm private requirements during frontend build #1

Merged
merged 2 commits into from
May 7, 2024

Conversation

iamsobanjaved
Copy link
Member

@iamsobanjaved iamsobanjaved commented May 2, 2024

  • Add facility to instal those requirements privately to MFEs which have only 2U-specific use-case.
  • Added tooling to copy JS-based config file from edx-internal to MFE root, it will help us in adding support for Datadog without involving community.

@iamsobanjaved iamsobanjaved marked this pull request as ready for review May 3, 2024 13:00
@@ -127,6 +153,17 @@ def create_version_file(self):
except IOError:
self.FAIL(1, 'Could not write to version file for app {}.'.format(self.app_name))

def copy_js_config_file_to_app_root(self, app_config, app_name):
""" Creates a copy of env.config.js(x) file to the app root """
source = app_config['JS_CONFIG_FILEPATH']
Copy link
Member

Choose a reason for hiding this comment

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

[suggestion] Should we only attempt to copy the JS_CONFIG_FILEPATH file if JS_CONFIG_FILEPATH exists? Otherwise, will this end up calling self.FAIL simply when an MFE does not define JS_CONFIG_FILEPATH?

We should ensure MFEs without JS_CONFIG_FILEPATH don't fail GoCD builds to be able to incrementally migrate MFEs.

def copy_js_config_file_to_app_root(self, app_config, app_name):
""" Creates a copy of env.config.js(x) file to the app root """
source = app_config['JS_CONFIG_FILEPATH']
destination = f"{app_name}/{app_config['LOCAL_CONFIG_FILENAME']}"
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for getting this work added in here. I'm wondering about this LOCAL_CONFIG_FILENAME field. I see it is being set to webpack.prod.config.js in the example. Does this mean the JS file will be copied to the webpack.prod.config.js file?

For JS file configuration to work properly, we would want this file to be copied to the root of the MFE. That being said, is there a desire/need to copy configuration into the webpack file?

Choose a reason for hiding this comment

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

I agree. I think destination = self.app_name is the correct directory. We are not trying to customize webpack.prod.config.js during production.

]
assert mock_create_version.call_count == 1
assert mock_shutil_copyfile.call_count == 1
# Verify that source is correct and destination is rightly formatted
mock_shutil_copyfile.assert_called_with('dummy/file/path/env.config.js', 'coolfrontend/webpack.prod.config.js')
Copy link
Member

Choose a reason for hiding this comment

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

Adding onto Max's question above about LOCAL_CONFIG_FILENAME, the copied file should be named env.config.js within the destination path (i.e., root of the MFE). The copied file should not be the Webpack configuration.

@@ -102,6 +120,14 @@ def get_npm_aliases_config(self):
self.LOG('No npm package aliases defined in config.')
return npm_aliases_config

def get_npm_private_config(self):
Copy link

@leangseu-edx leangseu-edx May 3, 2024

Choose a reason for hiding this comment

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

this can be achieved with NPM_ALIASES. I don't think we need to add another implementation.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW, the difference between NPM_ALIASES and NPM_PRIVATE is that dependencies in NPM_PRIVATE are not aliases. If we relied on NPM_ALIASES, the syntax might be somewhat unnecessarily verbose/confusing to define an NPM alias for the same package name it already has.

NPM_ALIASES:
  - "@datadog/browser-rum": "npm:@datadog/browser-rum@2"

versus:

NPM_PRIVATE:
  - "@datadog/browser-rum@2"

Also copy JS config file to app root in frontend build
@iamsobanjaved
Copy link
Member Author

Tests are green, coverage is failing due to rate-limiting issue of codecov.

def copy_js_config_file_to_app_root(self, app_config, app_name):
""" Creates a copy of env.config.js(x) file to the app root """
source = app_config.get('JS_CONFIG_FILEPATH')
filename = app_config.get('LOCAL_CONFIG_FILENAME', "env.config.js")
Copy link
Member

Choose a reason for hiding this comment

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

[nit/suggestion] It might be worth renaming LOCAL_CONFIG_FILENAME to LOCAL_JS_CONFIG_FILENAME so the config setting is explicitly referencing the JS config (versus some other config).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

def copy_js_config_file_to_app_root(self, app_config, app_name):
""" Creates a copy of env.config.js(x) file to the app root """
source = app_config.get('JS_CONFIG_FILEPATH')
filename = app_config.get('LOCAL_CONFIG_FILENAME', "env.config.js")
Copy link
Member

Choose a reason for hiding this comment

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

[sanity check] Will each individual MFEs be able to override the default LOCAL_JS_CONFIG_FILENAME of env.config.js, as needed?

For example, if an MFE relies on an env.stage.config.jsx file, the copied/replicated env.config file in the root of the MFE should also use the .jsx file extension. There would be a similar case if MFEs opted to use .ts or .tsx (TypeScript) extensions as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, they can override LOCAL_JS_CONFIG_FILENAME in their MFE's APP_CONFIG, also adjusted the test case to test overriding behaviour.

Comment on lines 160 to 161
# Skip if JS_CONFIG_FILEPATH not defined
if source:
Copy link
Member

Choose a reason for hiding this comment

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

nit: it might be slightly more readable/concise if you fail early when source is not defined, so the "happy path" logic doesn't get nested under a conditional block.

source = app_config.get('JS_CONFIG_FILEPATH')

# Skip if JS_CONFIG_FILEPATH not defined
if not source:
  return

filename = app_config.get('LOCAL_CONFIG_FILENAME', "env.config.js")
destination = f"{app_name}/{filename}"
try:
  shutil.copyfile(source, destination)
except FileNotFoundError:
  self.FAIL(1, f"Could not find '{source}' for copying for app {app_name}.")
except OSError:
  self.FAIL(1, f"Could not copy '{source}' to '{destination}', due to destination not writable.")

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

if not source:
return

filename = app_config.get('LOCAL_JS_CONFIG_FILENAME', "env.config.js")
Copy link
Member

Choose a reason for hiding this comment

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

nit: is it worth having one extra check on line 161 if app_config.get('LOCAL_JS_CONFIG_FILENAME') does not exist? Though it should exist since you're defining it in a common location applicable to all MFEs, an MFE could override it to be an empty config settings.

@iamsobanjaved iamsobanjaved merged commit d034d2f into edx:master May 7, 2024
2 of 3 checks passed
@iamsobanjaved iamsobanjaved deleted the iamsobanjaved/npm-private branch May 7, 2024 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants