Skip to content

Commit

Permalink
Cache git dependencies as wheels
Browse files Browse the repository at this point in the history
Currently, poetry install will clone, build and install every git
dependency when it's not present in the environment. This is OK for
developer's machines, but not OK for CI - there environment is always
fresh, and installing git dependencies takes significant time on each CI
run, especially if the dependency has C extensions that need to be
built.

This commit builds a wheel for every git dependency that has precise
reference hash in lock file and is not required to be in editable mode,
stores that wheel in a cache dir and will install from it instead of
cloning the repository again.
  • Loading branch information
maksbotan committed Feb 5, 2023
1 parent 53cdce0 commit b1ec957
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/poetry/installation/chef.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ def get_cache_directory_for_link(self, link: Link) -> Path:
self._env.marker_env["interpreter_version"].split(".")[:2]
)

return self._get_directory_from_hash(key_parts)

def get_cache_directory_for_git(self, url: str, ref: str) -> Path:
return self._get_directory_from_hash({"url": url, "ref": ref})

def _get_directory_from_hash(self, key_parts: object) -> Path:
key = hashlib.sha256(
json.dumps(
key_parts, sort_keys=True, separators=(",", ":"), ensure_ascii=True
Expand Down
23 changes: 20 additions & 3 deletions src/poetry/installation/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import itertools
import json
import os
import shutil
import threading

from concurrent.futures import ThreadPoolExecutor
Expand Down Expand Up @@ -502,7 +503,6 @@ def _install(self, operation: Install | Update) -> int:
cleanup_archive: bool = False
if package.source_type == "git":
archive = self._prepare_git_archive(operation)
cleanup_archive = True
elif package.source_type == "file":
archive = self._prepare_archive(operation)
elif package.source_type == "directory":
Expand Down Expand Up @@ -599,14 +599,25 @@ def _prepare_git_archive(self, operation: Install | Update) -> Path:
from poetry.vcs.git import Git

package = operation.package
reference = package.source_resolved_reference or package.source_reference
assert package.source_url is not None
assert reference is not None

cache_dir = self._chef.get_cache_directory_for_git(
package.source_url, reference
)
cache_dir.mkdir(parents=True, exist_ok=True)
cached_archive = next(cache_dir.glob("*.whl"), None)
if cached_archive is not None and not operation.package.develop:
return cached_archive

operation_message = self.get_operation_message(operation)

message = (
f" <fg=blue;options=bold>•</> {operation_message}: <info>Cloning...</info>"
)
self._write(operation, message)

assert package.source_url is not None
source = Git.clone(
url=package.source_url,
source_root=self._env.path / "src",
Expand All @@ -621,7 +632,13 @@ def _prepare_git_archive(self, operation: Install | Update) -> Path:

package._source_url = original_url

return archive
if operation.package.develop:
return archive
else:
cached_archive = cache_dir / archive.name
shutil.copy(archive, cached_archive)

return cached_archive

def _install_directory_without_wheel_installer(
self, operation: Install | Update
Expand Down

0 comments on commit b1ec957

Please sign in to comment.