def generate_npmrc_config_files( proxy_repo_url: str, username: str, password: str, subpaths: List[str], ) -> List[dict]: """ Generate one .npmrc config file for each subpath in request. If Nexus has a CA cert, it will also be added as a configuration file. The contents of all .nmprc files are the same except for the 'cafile' option, which defines the relative path from the app directory to the CA cert. :param str proxy_repo_url: url of the npm proxy repo :param str username: username with read access to the proxy repo :param str password: the password for the corresponding username :param list[str] subpaths: list of package subpaths in request :return: list of config files to be added to the request """ config_files = [] ca_cert = nexus.get_ca_cert() if ca_cert: # The custom CA will be called registry-ca.pem in the "app" directory ca_path = os.path.join("app", "registry-ca.pem") config_files.append(make_base64_config_file(ca_cert, ca_path)) for subpath in subpaths: if ca_cert: # Determine the relative path to the registry-ca.pem file custom_ca_path = os.path.relpath("registry-ca.pem", start=subpath) else: custom_ca_path = None npm_rc = generate_npmrc_content(proxy_repo_url, username, password, custom_ca_path=custom_ca_path) npm_rc_path = os.path.normpath(os.path.join("app", subpath, ".npmrc")) config_files.append(make_base64_config_file(npm_rc, npm_rc_path)) return config_files
def test_generate_npmrc_content(custom_ca_path): npm_rc = general_js.generate_npmrc_content( "http://nexus:8081/repository/cachito-npm-1/", "admin", "admin123", custom_ca_path=custom_ca_path, ) expected = textwrap.dedent("""\ registry=http://nexus:8081/repository/cachito-npm-1/ [email protected] always-auth=true _auth=YWRtaW46YWRtaW4xMjM= fetch-retries=5 fetch-retry-factor=2 strict-ssl=true """) if custom_ca_path: expected += f'cafile="{custom_ca_path}"\n' assert npm_rc == expected
def fetch_npm_source(request_id, package_configs=None): """ Resolve and fetch npm dependencies for a given request. This function uses the Python ``os.path`` library to manipulate paths, so the path to the configuration files may differ in format based on the system the Cachito worker is deployed on (i.e. Linux vs Windows). :param int request_id: the Cachito request ID this is for :param list package_configs: the list of optional package configurations submitted by the user :raise CachitoError: if the task fails """ if package_configs is None: package_configs = [] validate_npm_config() bundle_dir = RequestBundleDir(request_id) log.debug("Checking if the application source uses npm") subpaths = [ os.path.normpath(c["path"]) for c in package_configs if c.get("path") ] if not subpaths: # Default to the root of the application source subpaths = [os.curdir] _verify_npm_files(bundle_dir, subpaths) log.info("Configuring Nexus for npm for the request %d", request_id) set_request_state(request_id, "in_progress", "Configuring Nexus for npm") repo_name = get_npm_proxy_repo_name(request_id) prepare_nexus_for_js_request(repo_name) npm_config_files = [] downloaded_deps = set() for i, subpath in enumerate(subpaths): log.info("Fetching the npm dependencies for request %d in subpath %s", request_id, subpath) request = set_request_state( request_id, "in_progress", f'Fetching the npm dependencies at the "{subpath}" directory"', ) package_source_path = str(bundle_dir.app_subpath(subpath).source_dir) try: package_and_deps_info = resolve_npm(package_source_path, request, skip_deps=downloaded_deps) except CachitoError: log.exception("Failed to fetch npm dependencies for request %d", request_id) raise downloaded_deps = downloaded_deps | package_and_deps_info[ "downloaded_deps"] log.info( "Generating the npm configuration files for request %d in subpath %s", request_id, subpath, ) remote_package_source_path = os.path.normpath( os.path.join("app", subpath)) if package_and_deps_info["package.json"]: package_json_str = json.dumps( package_and_deps_info["package.json"], indent=2) npm_config_files.append({ "content": base64.b64encode( package_json_str.encode("utf-8")).decode("utf-8"), "path": os.path.join(remote_package_source_path, "package.json"), "type": "base64", }) if package_and_deps_info["lock_file"]: package_lock_str = json.dumps(package_and_deps_info["lock_file"], indent=2) lock_file_name = package_and_deps_info["lock_file_name"] npm_config_files.append({ "content": base64.b64encode( package_lock_str.encode("utf-8")).decode("utf-8"), "path": os.path.join(remote_package_source_path, lock_file_name), "type": "base64", }) if i == 0: env_vars = get_worker_config( ).cachito_default_environment_variables.get("npm", {}) else: env_vars = None package = package_and_deps_info["package"] update_request_with_package(request_id, package, env_vars, package_subpath=subpath) update_request_with_deps(request_id, package, package_and_deps_info["deps"]) log.info("Finalizing the Nexus configuration for npm for the request %d", request_id) set_request_state(request_id, "in_progress", "Finalizing the Nexus configuration for npm") username = get_npm_proxy_username(request_id) password = finalize_nexus_for_js_request(username, repo_name) log.info("Generating the .npmrc file(s)") ca_cert = nexus.get_ca_cert() if ca_cert: # The custom CA will be called registry-ca.pem in the "app" directory npm_config_files.append({ "content": base64.b64encode(ca_cert.encode("utf-8")).decode("utf-8"), "path": os.path.join("app", "registry-ca.pem"), "type": "base64", }) for subpath in subpaths: proxy_repo_url = get_npm_proxy_repo_url(request_id) if ca_cert: # Determine the relative path to the registry-ca.pem file custom_ca_path = os.path.relpath("registry-ca.pem", start=subpath) else: custom_ca_path = None npm_rc = generate_npmrc_content(proxy_repo_url, username, password, custom_ca_path=custom_ca_path) npm_config_files.append({ "content": base64.b64encode(npm_rc.encode("utf-8")).decode("utf-8"), "path": os.path.normpath(os.path.join("app", subpath, ".npmrc")), "type": "base64", }) update_request_with_config_files(request_id, npm_config_files)