-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathpre_conditions.py
More file actions
150 lines (118 loc) · 5.26 KB
/
pre_conditions.py
File metadata and controls
150 lines (118 loc) · 5.26 KB
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
import re
import typing
from functools import wraps
from packaging.version import Version
from language_formatters_pre_commit_hooks.utils import run_command
F = typing.TypeVar("F", bound=typing.Callable[..., int])
def _is_command_success(
*command_args: str,
) -> bool:
exit_status, _, _ = run_command(*command_args)
return exit_status == 0
class ToolNotInstalled(RuntimeError):
def __init__(self, tool_name: str, download_install_url: str) -> None:
self.tool_name = tool_name
self.download_install_url = download_install_url
def __str__(self) -> str:
return str(
"{tool_name} is required to run this pre-commit hook.\n"
"Make sure that you have it installed and available on your path.\n"
"Download/Install URL: {download_install_url}".format(
tool_name=self.tool_name,
download_install_url=self.download_install_url,
)
)
class _ToolRequired:
def __init__(
self,
tool_name: str,
check_command: typing.Callable[[typing.Optional[typing.Mapping[str, typing.Any]]], bool],
download_install_url: str,
extras: typing.Optional[typing.Mapping[str, typing.Any]] = None,
) -> None:
self.tool_name = tool_name
self.check_command = check_command
self.download_install_url = download_install_url
self.extras = extras
def is_tool_installed(self) -> bool:
return self.check_command(self.extras)
def assert_tool_installed(self) -> None:
if not self.is_tool_installed():
raise ToolNotInstalled(
tool_name=self.tool_name,
download_install_url=self.download_install_url,
)
def __call__(self, f: F) -> F:
@wraps(f)
def wrapper(*args: typing.Any, **kwargs: typing.Any) -> int:
self.assert_tool_installed()
return f(*args, **kwargs)
return wrapper # type: ignore
java_required = _ToolRequired(
tool_name="JRE",
check_command=lambda _: _is_command_success("java", "-version"),
download_install_url="https://www.java.com/en/download/",
)
golang_required = _ToolRequired(
tool_name="golang/gofmt",
check_command=lambda _: _is_command_success("go", "version"),
download_install_url="https://golang.org/doc/install#download",
)
rust_required = _ToolRequired(
tool_name="rustfmt",
check_command=(lambda _: _is_command_success("cargo", "fmt", "--", "--version")),
download_install_url="https://github.com/rust-lang-nursery/rustfmt#quick-start",
)
class UnableToVerifyJDKVersion(RuntimeError):
def __str__(self) -> str:
return "Unable to verify the JDK version" # pragma: no cover
def get_jdk_version() -> Version:
"""
Extract the version of the JDK accessible by the tool.
:raises UnableToVerifyJDKVersion: if it was not possible to gather the JDK version
This includes the case of `java` binary is not found in the path.
"""
_, _, stderr = run_command("java", "-XshowSettings:properties", "-version")
try:
java_property_line = next(line for line in stderr.splitlines() if re.match(r"^\s+java.version\s+=\s+[^s]+$", line))
return Version(java_property_line.split()[-1])
except Exception as e:
raise UnableToVerifyJDKVersion() from e
def assert_min_jdk_version(version: Version) -> None:
"""
Ensure that the version of the accessible JDK is at least at the provided version.
:raises UnableToVerifyJDKVersion: if it was not possible to gather the JDK version
This includes the case of `java` binary is not found in the path.
:raises ToolNotInstalled: if `java` binary is found in the path but the JDK version does not
respect the min version requirement
"""
_ToolRequired(
tool_name=f"JRE: min version {version}",
check_command=lambda extras: bool(extras and get_jdk_version() >= extras["min_sdk"]),
download_install_url="https://www.java.com/en/download/",
extras={"min_sdk": version},
).assert_tool_installed()
def assert_max_jdk_version(version: Version, *, inclusive: bool = False) -> None:
"""
Ensure that the version of the accessible JDK is at most at the provided version.
The inclusive parameter allows us to include or exclude the specified version:
* if `inclusive=True` then `get_jdk_version() <= version` is evaluated
* if `inclusive=False` then `get_jdk_version() < version` is evaluated
NOTE: The missing `=` in the operation
:raises UnableToVerifyJDKVersion: if it was not possible to gather the JDK version
This includes the case of `java` binary is not found in the path.
:raises ToolNotInstalled: if `java` binary is found in the path but the JDK version does not
respect the max version requirement
"""
if inclusive:
tool_name = f"JRE: version <= {version}"
else:
tool_name = f"JRE: version < {version}"
_ToolRequired(
tool_name=tool_name,
check_command=lambda extras: bool(
extras and (get_jdk_version() <= extras["max_sdk"] if inclusive else get_jdk_version() < extras["max_sdk"])
),
download_install_url="https://www.java.com/en/download/",
extras={"max_sdk": version},
).assert_tool_installed()