-
Notifications
You must be signed in to change notification settings - Fork 22
/
check_backcompat.py
145 lines (115 loc) · 4.38 KB
/
check_backcompat.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
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
"""
Check the current repo against all previous supported versions of µsort
If any previous version triggers sorting/formatting changes, we consider that
worthy of a major version bump, and should be reverted or at least reconsidered.
See the Versioning Guide for details.
TODO: include other projects as part of testing, like black, libcst, etc
"""
import json
import platform
import subprocess
import sys
import venv
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import List, Optional
from urllib.request import urlopen
from packaging.version import Version
REPO_ROOT = Path(__file__).parent.resolve()
MINIMUM_VERSION = Version("1.0.0")
PYPI_JSON_URL = "https://pypi.org/pypi/usort/json"
def get_current_version() -> Version:
with TemporaryDirectory() as td:
root = Path(td).resolve()
usort = setup_virtualenv(root)
proc = subprocess.run(
(usort, "--version"), encoding="utf-8", capture_output=True, check=True
)
return Version(proc.stdout.rpartition(" ")[-1])
def get_public_versions(
current_version: Version, minimum_version: Version
) -> List[Version]:
"""
Find all non-yanked versions of usort.
Limits results such that TARGET_VERSION <= CANDIDATE_VERSION <= CURRENT_VERSION
"""
with urlopen(PYPI_JSON_URL) as request:
data = json.loads(request.read())
versions: List[Version] = []
for version_str in data["releases"]:
version = Version(version_str)
if all(dist["yanked"] for dist in data["releases"][version_str]):
continue
if minimum_version <= version <= current_version:
versions.append(version)
return sorted(versions, reverse=True)
def setup_virtualenv(root: Path, version: Optional[Version] = None) -> Path:
"""
Create venv, install usort, return path to `usort` binary
"""
venv_path = root / f"venv-{version}" if version else root / "venv-local"
venv.create(venv_path, clear=True, with_pip=True)
bin_dir = (
venv_path / "Scripts" if platform.system() == "Windows" else venv_path / "bin"
)
python = bin_dir / "python"
subprocess.run((python, "-m", "pip", "-q", "install", "-U", "pip"), check=True)
if version:
subprocess.run(
(python, "-m", "pip", "-q", "install", "-U", f"usort=={version}"),
check=True,
)
else:
subprocess.run(
(python, "-m", "pip", "-q", "install", "-U", REPO_ROOT), check=True
)
return bin_dir / "usort"
def check_versions(versions: List[Version]) -> List[Version]:
"""
Format with local version, then check all released versions for regressions
"""
with TemporaryDirectory() as td:
root = Path(td).resolve()
try:
print("sorting with local version ...")
usort = setup_virtualenv(root)
subprocess.run((usort, "--version"), check=True)
subprocess.run((usort, "format", "usort"), check=True)
print("done\n")
except Exception as e:
return [("local", e)]
failures: List[Version] = []
for version in versions:
try:
print(f"checking version {version} ...")
usort = setup_virtualenv(root, version)
subprocess.run((usort, "--version"), check=True)
subprocess.run((usort, "check", "usort"), check=True)
print("clean\n")
except Exception as e:
failures.append((version, e))
return failures
def main() -> None:
current_version = get_current_version()
minimum_version = MINIMUM_VERSION
print(f"{current_version = !s}\n{minimum_version = !s}\n")
versions = get_public_versions(current_version, minimum_version)
if not versions:
print("error: no versions found")
sys.exit(1)
versions_str = "\n ".join(str(v) for v in versions)
print(f"discovered versions:\n {versions_str}\n")
failures = check_versions(versions)
if failures:
print("Sorting failed in versions:")
for version, exc in failures:
print(f" {version}: {exc}")
sys.exit(1)
else:
print("success!")
if __name__ == "__main__":
main()