|
10 | 10 | import subprocess |
11 | 11 | import tempfile |
12 | 12 | import zipfile |
| 13 | +from collections import defaultdict |
13 | 14 | from io import BytesIO |
14 | 15 | from pathlib import Path |
15 | 16 | from threading import Lock |
@@ -48,17 +49,21 @@ def __new__(cls): |
48 | 49 | def __init__(self, cache_directory=None): |
49 | 50 | self._cache_directory = cache_directory or self.DEFAULT_DISK_CACHE_DIRECTORY |
50 | 51 | Path(self._cache_directory).mkdir(exist_ok=True, parents=True) |
51 | | - # check az extension azure-devops installed |
| 52 | + # check az extension azure-devops installed. Install it if not installed. |
52 | 53 | process = subprocess.Popen( |
53 | | - "az artifacts --help", |
| 54 | + "az artifacts --help --yes", |
54 | 55 | shell=True, # nosec B602 |
55 | 56 | stdout=subprocess.PIPE, |
56 | 57 | stderr=subprocess.PIPE, |
57 | 58 | ) |
58 | 59 | process.communicate() |
59 | 60 | if process.returncode != 0: |
60 | | - subprocess.check_call("az extension add --name azure-devops", shell=True) |
| 61 | + raise RuntimeError( |
| 62 | + "Auto-installation failed. Please install azure-devops " |
| 63 | + "extension by 'az extension add --name azure-devops'." |
| 64 | + ) |
61 | 65 | self._artifacts_tool_path = None |
| 66 | + self._download_locks = defaultdict(Lock) |
62 | 67 |
|
63 | 68 | @property |
64 | 69 | def cache_directory(self): |
@@ -253,27 +258,29 @@ def get(self, feed, name, version, scope, organization=None, project=None, resol |
253 | 258 | / name |
254 | 259 | / version |
255 | 260 | ) |
256 | | - if self._check_artifacts(artifact_package_path): |
257 | | - # When the cache folder of artifact package exists, it's sure that the package has been downloaded. |
258 | | - return artifact_package_path.absolute().resolve() |
259 | | - if resolve: |
260 | | - check_sum_path = self._get_checksum_path(artifact_package_path) |
261 | | - if Path(check_sum_path).exists(): |
262 | | - os.unlink(check_sum_path) |
263 | | - if artifact_package_path.exists(): |
264 | | - # Remove invalid artifact package to avoid affecting download artifact. |
265 | | - temp_folder = tempfile.mktemp() # nosec B306 |
266 | | - os.rename(artifact_package_path, temp_folder) |
267 | | - shutil.rmtree(temp_folder) |
268 | | - # Download artifact |
269 | | - return self.set( |
270 | | - feed=feed, |
271 | | - name=name, |
272 | | - version=version, |
273 | | - organization=organization, |
274 | | - project=project, |
275 | | - scope=scope, |
276 | | - ) |
| 261 | + # Use lock to avoid downloading the same package at the same time. |
| 262 | + with self._download_locks[artifact_package_path]: |
| 263 | + if self._check_artifacts(artifact_package_path): |
| 264 | + # When the cache folder of artifact package exists, it's sure that the package has been downloaded. |
| 265 | + return artifact_package_path.absolute().resolve() |
| 266 | + if resolve: |
| 267 | + check_sum_path = self._get_checksum_path(artifact_package_path) |
| 268 | + if Path(check_sum_path).exists(): |
| 269 | + os.unlink(check_sum_path) |
| 270 | + if artifact_package_path.exists(): |
| 271 | + # Remove invalid artifact package to avoid affecting download artifact. |
| 272 | + temp_folder = tempfile.mktemp() # nosec B306 |
| 273 | + os.rename(artifact_package_path, temp_folder) |
| 274 | + shutil.rmtree(temp_folder) |
| 275 | + # Download artifact |
| 276 | + return self.set( |
| 277 | + feed=feed, |
| 278 | + name=name, |
| 279 | + version=version, |
| 280 | + organization=organization, |
| 281 | + project=project, |
| 282 | + scope=scope, |
| 283 | + ) |
277 | 284 | return None |
278 | 285 |
|
279 | 286 | def set(self, feed, name, version, scope, organization=None, project=None): |
|
0 commit comments