Example #1
0
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
Example #2
0
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)
Example #3
0
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)