def test_resolve_yarn(mock_download_deps, mock_get_repo_url, mock_get_package_and_deps): n_pop_calls = 0 def dict_pop_mocker(): expected_keys = ["version_in_nexus", "bundled"] def mock_pop(key): nonlocal n_pop_calls n_pop_calls += 1 if expected_keys: assert key == expected_keys.pop() else: assert False return mock_pop mock_package = mock.Mock() mock_deps = [mock.Mock()] for mock_dep in mock_deps: mock_dep.pop.side_effect = dict_pop_mocker() mock_get_package_and_deps.return_value = { "package": mock_package, "deps": mock_deps } rv = yarn.resolve_yarn("/some/path", {"id": 1}, skip_deps={"foobar"}) assert rv == mock_get_package_and_deps.return_value mock_get_package_and_deps.assert_called_once_with( Path("/some/path/package.json"), Path("/some/path/yarn.lock")) mock_get_repo_url.assert_called_once_with(1) mock_download_deps.assert_called_once_with(1, mock_deps, mock_get_repo_url.return_value, skip_deps={"foobar"}, pkg_manager="yarn") assert n_pop_calls == len(mock_deps) * 2
def fetch_yarn_source(request_id: int, package_configs: List[dict] = None): """ Resolve and fetch yarn 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_yarn_config() bundle_dir = RequestBundleDir(request_id) 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_yarn_files(bundle_dir, subpaths) log.info("Configuring Nexus for yarn for the request %d", request_id) set_request_state(request_id, "in_progress", "Configuring Nexus for yarn") repo_name = get_yarn_proxy_repo_name(request_id) prepare_nexus_for_js_request(repo_name) yarn_config_files = [] downloaded_deps = set() for i, subpath in enumerate(subpaths): log.info("Fetching the yarn dependencies for request %d in subpath %s", request_id, subpath) set_request_state( request_id, "in_progress", f'Fetching the yarn dependencies at the "{subpath}" directory', ) request = get_request(request_id) package_source_path = str(bundle_dir.app_subpath(subpath).source_dir) try: package_and_deps_info = resolve_yarn(package_source_path, request, skip_deps=downloaded_deps) except CachitoError: log.exception("Failed to fetch yarn dependencies for request %d", request_id) raise downloaded_deps = downloaded_deps | package_and_deps_info[ "downloaded_deps"] log.info( "Generating the yarn 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) package_json_path = os.path.join(remote_package_source_path, "package.json") yarn_config_files.append( make_base64_config_file(package_json_str, package_json_path)) if package_and_deps_info["lock_file"]: yarn_lock_str = _yarn_lock_to_str( package_and_deps_info["lock_file"]) yarn_lock_path = os.path.join(remote_package_source_path, "yarn.lock") yarn_config_files.append( make_base64_config_file(yarn_lock_str, yarn_lock_path)) if i == 0: default_env = get_worker_config( ).cachito_default_environment_variables env_vars = { **default_env.get("npm", {}), **default_env.get("yarn", {}) } 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 yarn for the request %d", request_id) set_request_state(request_id, "in_progress", "Finalizing the Nexus configuration for yarn") username = get_yarn_proxy_repo_username(request_id) password = finalize_nexus_for_js_request(username, repo_name) log.info("Generating the .npmrc file(s)") proxy_repo_url = get_yarn_proxy_repo_url(request_id) yarn_config_files.extend( generate_npmrc_config_files(proxy_repo_url, username, password, subpaths)) log.info("Adding empty .yarnrc file(s)") for subpath in subpaths: yarnrc_path = os.path.normpath(os.path.join("app", subpath, ".yarnrc")) yarn_config_files.append(make_base64_config_file("", yarnrc_path)) update_request_with_config_files(request_id, yarn_config_files)
def test_resolve_yarn( mock_replace_yarnlock, mock_replace_packjson, mock_set_proxy_urls, mock_get_repo_name, mock_download_deps, mock_get_repo_url, mock_get_package_and_deps, have_nexus_replacements, any_urls_in_yarn_lock, ): n_pop_calls = 0 def dict_pop_mocker(): expected_keys = ["version_in_nexus", "bundled"] def mock_pop(key): nonlocal n_pop_calls n_pop_calls += 1 if expected_keys: assert key == expected_keys.pop() else: assert False return mock_pop mock_package = mock.Mock() mock_deps = [mock.Mock()] for mock_dep in mock_deps: mock_dep.pop.side_effect = dict_pop_mocker() mock_package_json = mock.Mock() mock_yarn_lock = mock.Mock() mock_nexus_replacements = {"foo": {}} if have_nexus_replacements else {} mock_get_package_and_deps.return_value = { "package": mock_package, "deps": mock_deps, "package.json": mock_package_json, "lock_file": mock_yarn_lock, "nexus_replacements": mock_nexus_replacements, } if any_urls_in_yarn_lock: mock_set_proxy_urls.return_value = True expect_yarn_lock = mock_replace_yarnlock.return_value else: mock_set_proxy_urls.return_value = False expect_yarn_lock = None rv = yarn.resolve_yarn("/some/path", {"id": 1}, skip_deps={"foobar"}) assert rv == { "package": mock_package, "deps": mock_deps, "downloaded_deps": mock_download_deps.return_value, "package.json": mock_replace_packjson.return_value, "lock_file": expect_yarn_lock, } mock_get_package_and_deps.assert_called_once_with( Path("/some/path/package.json"), Path("/some/path/yarn.lock")) mock_get_repo_url.assert_called_once_with(1) mock_download_deps.assert_called_once_with(1, mock_deps, mock_get_repo_url.return_value, skip_deps={"foobar"}, pkg_manager="yarn") assert n_pop_calls == len(mock_deps) * 2 if have_nexus_replacements: mock_get_repo_name.assert_called_once_with(1) mock_replace_packjson.assert_called_once_with(mock_package_json, mock_nexus_replacements) mock_replace_yarnlock.assert_called_once_with(mock_yarn_lock, mock_nexus_replacements) mock_set_proxy_urls.assert_called_once_with( mock_replace_yarnlock.return_value, mock_get_repo_name.return_value)