-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
git.py
153 lines (127 loc) · 4.74 KB
/
git.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import os
import hashlib
from typing import List, Optional
from dbt.clients import git, system
from dbt.config import Project
from dbt.contracts.project import (
ProjectPackageMetadata,
GitPackage,
)
from dbt.deps.base import PinnedPackage, UnpinnedPackage, get_downloads_path
from dbt.exceptions import ExecutableError, MultipleVersionGitDepsError
from dbt.events.functions import fire_event, warn_or_error
from dbt.events.types import EnsureGitInstalled, DepsUnpinned
def md5sum(s: str):
return hashlib.md5(s.encode("latin-1")).hexdigest()
class GitPackageMixin:
def __init__(self, git: str) -> None:
super().__init__()
self.git = git
@property
def name(self):
return self.git
def source_type(self) -> str:
return "git"
class GitPinnedPackage(GitPackageMixin, PinnedPackage):
def __init__(
self,
git: str,
revision: str,
warn_unpinned: bool = True,
subdirectory: Optional[str] = None,
) -> None:
super().__init__(git)
self.revision = revision
self.warn_unpinned = warn_unpinned
self.subdirectory = subdirectory
self._checkout_name = md5sum(self.git)
def get_version(self):
return self.revision
def get_subdirectory(self):
return self.subdirectory
def nice_version_name(self):
if self.revision == "HEAD":
return "HEAD (default revision)"
else:
return "revision {}".format(self.revision)
def _checkout(self):
"""Performs a shallow clone of the repository into the downloads
directory. This function can be called repeatedly. If the project has
already been checked out at this version, it will be a no-op. Returns
the path to the checked out directory."""
try:
dir_ = git.clone_and_checkout(
self.git,
get_downloads_path(),
revision=self.revision,
dirname=self._checkout_name,
subdirectory=self.subdirectory,
)
except ExecutableError as exc:
if exc.cmd and exc.cmd[0] == "git":
fire_event(EnsureGitInstalled())
raise
return os.path.join(get_downloads_path(), dir_)
def _fetch_metadata(self, project, renderer) -> ProjectPackageMetadata:
path = self._checkout()
if (self.revision == "HEAD" or self.revision in ("main", "master")) and self.warn_unpinned:
warn_or_error(DepsUnpinned(git=self.git))
loaded = Project.from_project_root(path, renderer)
return ProjectPackageMetadata.from_project(loaded)
def install(self, project, renderer):
dest_path = self.get_installation_path(project, renderer)
if os.path.exists(dest_path):
if system.path_is_symlink(dest_path):
system.remove_file(dest_path)
else:
system.rmdir(dest_path)
system.move(self._checkout(), dest_path)
class GitUnpinnedPackage(GitPackageMixin, UnpinnedPackage[GitPinnedPackage]):
def __init__(
self,
git: str,
revisions: List[str],
warn_unpinned: bool = True,
subdirectory: Optional[str] = None,
) -> None:
super().__init__(git)
self.revisions = revisions
self.warn_unpinned = warn_unpinned
self.subdirectory = subdirectory
@classmethod
def from_contract(cls, contract: GitPackage) -> "GitUnpinnedPackage":
revisions = contract.get_revisions()
# we want to map None -> True
warn_unpinned = contract.warn_unpinned is not False
return cls(
git=contract.git,
revisions=revisions,
warn_unpinned=warn_unpinned,
subdirectory=contract.subdirectory,
)
def all_names(self) -> List[str]:
if self.git.endswith(".git"):
other = self.git[:-4]
else:
other = self.git + ".git"
return [self.git, other]
def incorporate(self, other: "GitUnpinnedPackage") -> "GitUnpinnedPackage":
warn_unpinned = self.warn_unpinned and other.warn_unpinned
return GitUnpinnedPackage(
git=self.git,
revisions=self.revisions + other.revisions,
warn_unpinned=warn_unpinned,
subdirectory=self.subdirectory,
)
def resolved(self) -> GitPinnedPackage:
requested = set(self.revisions)
if len(requested) == 0:
requested = {"HEAD"}
elif len(requested) > 1:
raise MultipleVersionGitDepsError(self.git, requested)
return GitPinnedPackage(
git=self.git,
revision=requested.pop(),
warn_unpinned=self.warn_unpinned,
subdirectory=self.subdirectory,
)