diff --git a/docs/userguides/dependencies.md b/docs/userguides/dependencies.md index 17192d025c..b546c83ecc 100644 --- a/docs/userguides/dependencies.md +++ b/docs/userguides/dependencies.md @@ -62,6 +62,16 @@ This is helpful when: - When there is not a suitable `DependencyAPI` implementation available for downloading your dependency. - Testing the framework. +You can also reference local project manifests and use those as dependencies. +To do this, use a local value pointing to the manifest file, like this: + +```yaml +dependencies: + - name: MyDependency + local: ./my-dependency.json + version: 1.0.0 +``` + ### NPM You can use dependencies from NPM. diff --git a/src/ape/api/projects.py b/src/ape/api/projects.py index a213382252..8e91399b77 100644 --- a/src/ape/api/projects.py +++ b/src/ape/api/projects.py @@ -11,6 +11,7 @@ from packaging.version import InvalidVersion, Version from ape._pydantic_compat import ValidationError +from ape.exceptions import ProjectError from ape.logging import logger from ape.utils import ( BaseInterfaceModel, @@ -395,6 +396,31 @@ def _extract_local_manifest( if cached_manifest: return cached_manifest + if project_path.is_file() and project_path.suffix == ".json": + try: + manifest = PackageManifest.parse_file(project_path) + + except ValueError as err: + if project_path.parent.is_dir(): + project_path = project_path.parent + + else: + raise ProjectError(f"Invalid manifest file: '{project_path}'.") from err + + else: + # Was given a path to a manifest JSON. + self._write_manifest_to_cache(manifest) + return manifest + + elif (project_path.parent / project_path.name.replace("-", "_")).is_dir(): + project_path = project_path.parent / project_path.name.replace("-", "_") + + elif (project_path.parent / project_path.name.replace("_", "-")).is_dir(): + project_path = project_path.parent / project_path.name.replace("_", "-") + + elif project_path.parent.is_dir(): + project_path = project_path.parent + # NOTE: Dependencies are not compiled here. Instead, the sources are packaged # for later usage via imports. For legacy reasons, many dependency-esque projects # are not meant to compile on their own. diff --git a/tests/integration/cli/projects/with-dependencies/ape-config.yaml b/tests/integration/cli/projects/with-dependencies/ape-config.yaml index 07f8e7f4b0..7d44091fad 100644 --- a/tests/integration/cli/projects/with-dependencies/ape-config.yaml +++ b/tests/integration/cli/projects/with-dependencies/ape-config.yaml @@ -15,3 +15,6 @@ dependencies: - name: renamed-contracts-folder-specified-in-config local: ./renamed_contracts_folder_specified_in_config + + - name: manifest-dependency + local: ./manifest_dependency.json diff --git a/tests/integration/cli/projects/with-dependencies/manifest_dependency.json b/tests/integration/cli/projects/with-dependencies/manifest_dependency.json new file mode 100644 index 0000000000..73894ec3ad --- /dev/null +++ b/tests/integration/cli/projects/with-dependencies/manifest_dependency.json @@ -0,0 +1 @@ +{"contractTypes":{"manifest-dependency":{"abi":[{"stateMutability":"nonpayable","type":"fallback"}],"contractName":"manifest-dependency","sourceId":"manifest-dependency.json"}},"manifest":"ethpm/3","sources":{"manifest-dependency.json":{"checksum":{"algorithm":"md5","hash":"0xaaf11362c066814f73d7db766ade0a0c"},"content":"[\n {\"name\":\"foo\",\"type\":\"fallback\", \"stateMutability\":\"nonpayable\"}\n]\n","imports":[],"references":[],"urls":[]}}} diff --git a/tests/integration/cli/test_compile.py b/tests/integration/cli/test_compile.py index 3736ed2137..c2a2af1446 100644 --- a/tests/integration/cli/test_compile.py +++ b/tests/integration/cli/test_compile.py @@ -240,6 +240,7 @@ def test_compile_with_dependency(ape_cli, runner, project, contract_path): "containing-sub-dependencies", "renamed-complex-contracts-folder", "renamed-contracts-folder-specified-in-config", + "manifest-dependency", ): assert name in list(project.dependencies.keys()) dependency = project.dependencies[name]["local"]