Skip to content

Commit

Permalink
Generic fetcher: Init package manager
Browse files Browse the repository at this point in the history
Introduces new package manager that will be able to pre-fetch
generic dependencies from the internet. This change contains
only the general structure without any functionality yet.

Signed-off-by: Jan Koscielniak <[email protected]>
  • Loading branch information
kosciCZ authored and brunoapimentel committed Oct 8, 2024
1 parent b289c91 commit 9508fbb
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 6 deletions.
16 changes: 15 additions & 1 deletion cachi2/core/models/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def show_error(error: "ErrorDict") -> str:


# Supported package managers
PackageManagerType = Literal["bundler", "gomod", "npm", "pip", "rpm", "yarn", "yarn-classic"]
PackageManagerType = Literal[
"bundler", "generic", "gomod", "npm", "pip", "rpm", "yarn", "yarn-classic"
]

Flag = Literal[
"cgo-disable", "dev-package-managers", "force-gomod-tidy", "gomod-vendor", "gomod-vendor-check"
Expand All @@ -75,6 +77,12 @@ class BundlerPackageInput(_PackageInputBase):
type: Literal["bundler"]


class GenericPackageInput(_PackageInputBase):
"""Accepted input for generic package."""

type: Literal["generic"]


class GomodPackageInput(_PackageInputBase):
"""Accepted input for a gomod package."""

Expand Down Expand Up @@ -183,6 +191,7 @@ class YarnClassicPackageInput(_PackageInputBase):
PackageInput = Annotated[
Union[
BundlerPackageInput,
GenericPackageInput,
GomodPackageInput,
NpmPackageInput,
PipPackageInput,
Expand Down Expand Up @@ -259,6 +268,11 @@ def bundler_packages(self) -> list[BundlerPackageInput]:
"""Get the bundler packages specified for this request."""
return self._packages_by_type(BundlerPackageInput)

@property
def generic_packages(self) -> list[GenericPackageInput]:
"""Get the generic packages specified for this request."""
return self._packages_by_type(GenericPackageInput)

@property
def gomod_packages(self) -> list[GomodPackageInput]:
"""Get the gomod packages specified for this request."""
Expand Down
33 changes: 33 additions & 0 deletions cachi2/core/package_managers/generic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from cachi2.core.errors import PackageRejected
from cachi2.core.models.input import Request
from cachi2.core.models.output import RequestOutput
from cachi2.core.models.sbom import Component
from cachi2.core.rooted_path import RootedPath

DEFAULT_LOCKFILE_NAME = "cachi2_generic.yaml"
DEFAULT_DEPS_DIR = "deps/generic"


def fetch_generic_source(request: Request) -> RequestOutput:
"""
Resolve and fetch generic dependencies for a given request.
:param request: the request to process
"""
components = []
for package in request.generic_packages:
path = request.source_dir.join_within_root(package.path)
components.extend(_resolve_generic_lockfile(path, request.output_dir))
return RequestOutput.from_obj_list(components=components)


def _resolve_generic_lockfile(source_dir: RootedPath, output_dir: RootedPath) -> list[Component]:
if not source_dir.join_within_root(DEFAULT_LOCKFILE_NAME).path.exists():
raise PackageRejected(
f"Cachi2 generic lockfile '{DEFAULT_LOCKFILE_NAME}' missing, refusing to continue.",
solution=(
f"Make sure your repository has cachi2 generic lockfile '{DEFAULT_LOCKFILE_NAME}' checked in "
"to the repository."
),
)
return []
3 changes: 2 additions & 1 deletion cachi2/core/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from cachi2.core.errors import UnsupportedFeature
from cachi2.core.models.input import PackageManagerType, Request
from cachi2.core.models.output import RequestOutput
from cachi2.core.package_managers import bundler, gomod, npm, pip, rpm, yarn, yarn_classic
from cachi2.core.package_managers import bundler, generic, gomod, npm, pip, rpm, yarn, yarn_classic
from cachi2.core.rooted_path import RootedPath
from cachi2.core.utils import copy_directory

Expand All @@ -25,6 +25,7 @@
"bundler": bundler.fetch_bundler_source,
"rpm": rpm.fetch_rpm_source,
"yarn-classic": yarn_classic.fetch_yarn_source,
"generic": generic.fetch_generic_source,
}

# This is *only* used to provide a list for `cachi2 --version`
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/models/test_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_valid_packages(self, input_data: dict[str, Any], expect_data: dict[str,
),
pytest.param(
{"type": "go-package"},
r"Input tag 'go-package' found using 'type' does not match any of the expected tags: 'bundler', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
r"Input tag 'go-package' found using 'type' does not match any of the expected tags: 'bundler', 'generic', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
id="incorrect_type_tag",
),
pytest.param(
Expand Down
48 changes: 48 additions & 0 deletions tests/unit/package_managers/test_generic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from unittest import mock

import pytest

from cachi2.core.errors import PackageRejected
from cachi2.core.models.input import GenericPackageInput
from cachi2.core.models.sbom import Component
from cachi2.core.package_managers.generic import (
DEFAULT_LOCKFILE_NAME,
_resolve_generic_lockfile,
fetch_generic_source,
)
from cachi2.core.rooted_path import RootedPath


@pytest.mark.parametrize(
["model_input", "components"],
[
pytest.param(GenericPackageInput.model_construct(type="generic"), [], id="single_input"),
],
)
@mock.patch("cachi2.core.package_managers.rpm.main.RequestOutput.from_obj_list")
@mock.patch("cachi2.core.package_managers.generic._resolve_generic_lockfile")
def test_fetch_generic_source(
mock_resolve_generic_lockfile: mock.Mock,
mock_from_obj_list: mock.Mock,
model_input: GenericPackageInput,
components: list[Component],
) -> None:

mock_resolve_generic_lockfile.return_value = components

mock_request = mock.Mock()
mock_request.generic_packages = [model_input]

fetch_generic_source(mock_request)

mock_resolve_generic_lockfile.assert_called()
mock_from_obj_list.assert_called_with(components=components)


def test_resolve_generic_no_lockfile(rooted_tmp_path: RootedPath) -> None:
with pytest.raises(PackageRejected) as exc_info:
_resolve_generic_lockfile(rooted_tmp_path, rooted_tmp_path)
assert (
f"Cachi2 generic lockfile '{DEFAULT_LOCKFILE_NAME}' missing, refusing to continue"
in str(exc_info.value)
)
6 changes: 3 additions & 3 deletions tests/unit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,23 +358,23 @@ def test_specify_packages(
[
"Error: InvalidInput: 1 validation error for user input",
"packages -> 0",
"Input tag 'idk' found using 'type' does not match any of the expected tags: 'bundler', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
"Input tag 'idk' found using 'type' does not match any of the expected tags: 'bundler', 'generic', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
],
),
(
'[{"type": "idk"}]',
[
"Error: InvalidInput: 1 validation error for user input",
"packages -> 0",
"Input tag 'idk' found using 'type' does not match any of the expected tags: 'bundler', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
"Input tag 'idk' found using 'type' does not match any of the expected tags: 'bundler', 'generic', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
],
),
(
'{"packages": [{"type": "idk"}]}',
[
"Error: InvalidInput: 1 validation error for user input",
"packages -> 0",
"Input tag 'idk' found using 'type' does not match any of the expected tags: 'bundler', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
"Input tag 'idk' found using 'type' does not match any of the expected tags: 'bundler', 'generic', 'gomod', 'npm', 'pip', 'rpm', 'yarn-classic', 'yarn'",
],
),
# Missing package type
Expand Down

0 comments on commit 9508fbb

Please sign in to comment.