fosslight-util 2.2.2__tar.gz → 2.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {fosslight_util-2.2.2/src/fosslight_util.egg-info → fosslight_util-2.2.3}/PKG-INFO +1 -1
  2. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/pyproject.toml +1 -1
  3. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/_get_downloadable_url.py +135 -1
  4. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/constant.py +2 -0
  5. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/help.py +4 -0
  6. {fosslight_util-2.2.2 → fosslight_util-2.2.3/src/fosslight_util.egg-info}/PKG-INFO +1 -1
  7. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util.egg-info/SOURCES.txt +1 -0
  8. fosslight_util-2.2.3/tests/test_download_packagist.py +175 -0
  9. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/LICENSE +0 -0
  10. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/MANIFEST.in +0 -0
  11. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/README.md +0 -0
  12. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/setup.cfg +0 -0
  13. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/__init__.py +0 -0
  14. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/compare_yaml.py +0 -0
  15. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/correct.py +0 -0
  16. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/cover.py +0 -0
  17. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/download.py +0 -0
  18. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/exclude.py +0 -0
  19. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/get_pom_license.py +0 -0
  20. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/oss_item.py +0 -0
  21. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/output_format.py +0 -0
  22. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/parsing_yaml.py +0 -0
  23. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/read_excel.py +0 -0
  24. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/resources/frequentLicenselist.json +0 -0
  25. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/resources/frequent_license_nick_list.json +0 -0
  26. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/resources/licenses.json +0 -0
  27. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/set_log.py +0 -0
  28. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/spdx_licenses.py +0 -0
  29. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/time.py +0 -0
  30. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/timer_thread.py +0 -0
  31. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_cyclonedx.py +0 -0
  32. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_excel.py +0 -0
  33. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_opossum.py +0 -0
  34. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_scancodejson.py +0 -0
  35. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_spdx.py +0 -0
  36. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_txt.py +0 -0
  37. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util/write_yaml.py +0 -0
  38. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util.egg-info/dependency_links.txt +0 -0
  39. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util.egg-info/entry_points.txt +0 -0
  40. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util.egg-info/requires.txt +0 -0
  41. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/src/fosslight_util.egg-info/top_level.txt +0 -0
  42. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_cyclonedx.py +0 -0
  43. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_decide_checkout.py +0 -0
  44. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_download.py +0 -0
  45. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_download_version_hint.py +0 -0
  46. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_opossum.py +0 -0
  47. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_spdx_licenses.py +0 -0
  48. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_text.py +0 -0
  49. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_write_excel_hidden_sheet.py +0 -0
  50. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_write_output.py +0 -0
  51. {fosslight_util-2.2.2 → fosslight_util-2.2.3}/tests/test_write_yaml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_util
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: FOSSLight Util
5
5
  Author: LG Electronics
6
6
  License-Expression: Apache-2.0
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "fosslight_util"
10
- version = "2.2.2"
10
+ version = "2.2.3"
11
11
  description = "FOSSLight Util"
12
12
  readme = "README.md"
13
13
  license = "Apache-2.0"
@@ -203,6 +203,79 @@ def _resolve_debian_search_to_source_tarball(
203
203
  return "", ""
204
204
 
205
205
 
206
+ def _fetch_packagist_packages(package_name: str) -> list:
207
+ """Return package version entries from Packagist Composer v2 metadata API."""
208
+ try:
209
+ r = requests.get(f"https://repo.packagist.org/p2/{package_name}.json", timeout=10)
210
+ if r.status_code != 200:
211
+ return []
212
+ return r.json().get("packages", {}).get(package_name, []) or []
213
+ except Exception as e:
214
+ logger.info(f"Fail to fetch packagist metadata ({package_name}): {e}")
215
+ return []
216
+
217
+
218
+ def _packagist_version_candidates(version: str) -> list[str]:
219
+ v = (version or "").strip()
220
+ if not v:
221
+ return []
222
+ no_v = v[1:] if v.lower().startswith("v") else v
223
+ candidates = [v, no_v, f"v{no_v}"]
224
+ return [c for c in dict.fromkeys(candidates) if c]
225
+
226
+
227
+ def _find_packagist_package_entry(packages: list, version: str = ""):
228
+ """Pick a Packagist package entry for version, or the newest entry when version is empty."""
229
+ if not packages:
230
+ return None
231
+ if not version:
232
+ return packages[0]
233
+ candidates = _packagist_version_candidates(version)
234
+ candidate_set = set(candidates)
235
+ candidate_no_v = {c[1:] if c.lower().startswith("v") else c for c in candidates}
236
+ for pkg in packages:
237
+ ver = (pkg.get("version") or "").strip()
238
+ norm = (pkg.get("version_normalized") or "").strip()
239
+ ver_no_v = ver[1:] if ver.lower().startswith("v") else ver
240
+ if ver in candidate_set or norm in candidate_set or ver_no_v in candidate_no_v:
241
+ return pkg
242
+ return None
243
+
244
+
245
+ def _archive_url_from_packagist_source(source: dict) -> str:
246
+ """Build a downloadable archive URL from Composer source metadata when dist is missing."""
247
+ if not source or source.get("type") != "git":
248
+ return ""
249
+ src_url = (source.get("url") or "").strip()
250
+ ref = (source.get("reference") or "").strip()
251
+ if not src_url or not ref:
252
+ return ""
253
+
254
+ github = re.match(
255
+ r"https?://(?:www\.)?github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$",
256
+ src_url,
257
+ re.IGNORECASE,
258
+ )
259
+ if github:
260
+ owner, repo = github.group(1), github.group(2)
261
+ if repo.endswith(".git"):
262
+ repo = repo[:-4]
263
+ return f"https://github.com/{owner}/{repo}/archive/{ref}.zip"
264
+
265
+ gitlab = re.match(
266
+ r"https?://(?:www\.)?gitlab\.com/([^/]+)/([^/]+?)(?:\.git)?/?$",
267
+ src_url,
268
+ re.IGNORECASE,
269
+ )
270
+ if gitlab:
271
+ owner, repo = gitlab.group(1), gitlab.group(2)
272
+ if repo.endswith(".git"):
273
+ repo = repo[:-4]
274
+ return f"https://gitlab.com/{owner}/{repo}/-/archive/{ref}/{repo}-{ref}.zip"
275
+
276
+ return ""
277
+
278
+
206
279
  def version_exists(pkg_type, origin_name, version):
207
280
  try:
208
281
  if pkg_type in ['npm', 'npm2']:
@@ -234,6 +307,9 @@ def version_exists(pkg_type, origin_name, version):
234
307
  if r.status_code == 200:
235
308
  listed = r.text.splitlines()
236
309
  return version in listed
310
+ elif pkg_type == 'packagist':
311
+ packages = _fetch_packagist_packages(origin_name)
312
+ return _find_packagist_package_entry(packages, version) is not None
237
313
  except Exception as e:
238
314
  logger.info(f'version_exists check failed ({pkg_type}:{origin_name}:{version}) {e}')
239
315
  return True
@@ -311,6 +387,9 @@ def extract_name_version_from_link(link, checkout_version):
311
387
  elif key == "cargo":
312
388
  oss_name = f"cargo:{origin_name}"
313
389
  oss_version = match.group(2)
390
+ elif key == "packagist":
391
+ oss_name = f"packagist:{origin_name}"
392
+ oss_version = (match.group(2) or match.group(3) or "").strip()
314
393
  except Exception as ex:
315
394
  logger.info(f"extract_name_version_from_link {key}:{ex}")
316
395
  if oss_name:
@@ -323,7 +402,7 @@ def extract_name_version_from_link(link, checkout_version):
323
402
  need_latest = False
324
403
  if not oss_version and checkout_version:
325
404
  oss_version = checkout_version.strip()
326
- if pkg_type in ["pypi", "maven", "npm", "npm2", "pub", "go"]:
405
+ if pkg_type in ["pypi", "maven", "npm", "npm2", "pub", "go", "packagist"]:
327
406
  if oss_version:
328
407
  try:
329
408
  if not version_exists(pkg_type, origin_name, oss_version):
@@ -445,6 +524,8 @@ def get_new_link_with_version(link, pkg_type, oss_name, oss_version):
445
524
  link = f'https://pkg.go.dev/{oss_name}@{oss_version}'
446
525
  elif pkg_type == "cargo":
447
526
  link = f'https://crates.io/crates/{oss_name}/{oss_version}'
527
+ elif pkg_type == "packagist":
528
+ link = f'https://packagist.org/packages/{oss_name}/{oss_version}'
448
529
  return link
449
530
 
450
531
 
@@ -513,6 +594,11 @@ def get_latest_package_version(link, pkg_type, oss_name):
513
594
  find_version = go_response.json().get('Version')
514
595
  if find_version.startswith('v'):
515
596
  find_version = find_version[1:]
597
+ elif pkg_type == 'packagist':
598
+ packages = _fetch_packagist_packages(oss_name)
599
+ latest_pkg = _find_packagist_package_entry(packages)
600
+ if latest_pkg:
601
+ find_version = (latest_pkg.get('version') or '').strip()
516
602
  except Exception as e:
517
603
  logger.info(f'Fail to get latest package version({link}:{e})')
518
604
  return find_version
@@ -546,9 +632,57 @@ def get_downloadable_url(link, checkout_version):
546
632
  ret, result_link = get_download_location_for_go(new_link)
547
633
  elif pkg_type == "cargo":
548
634
  ret, result_link = get_download_location_for_cargo(new_link)
635
+ elif pkg_type == "packagist" or new_link.startswith('packagist.org/packages/'):
636
+ ret, result_link = get_download_location_for_packagist(new_link)
549
637
  return ret, result_link, oss_name, oss_version, pkg_type
550
638
 
551
639
 
640
+ def get_download_location_for_packagist(link):
641
+ """
642
+ Resolve Packagist package page URL to a direct source archive URL.
643
+
644
+ Input examples (https:// stripped by caller):
645
+ packagist.org/packages/mustache/mustache
646
+ packagist.org/packages/mustache/mustache/v2.14.2
647
+ packagist.org/packages/mustache/mustache#v2.14.2
648
+ """
649
+ ret = False
650
+ new_link = ''
651
+
652
+ try:
653
+ matched = re.search(
654
+ r'packagist\.org/packages/([^/]+/[^/#?]+)(?:/([^/#?]+))?(?:#v?([^/#?]*))?',
655
+ link,
656
+ )
657
+ if not matched:
658
+ return False, ''
659
+
660
+ package_name = matched.group(1)
661
+ oss_version = (matched.group(2) or matched.group(3) or '').strip()
662
+ packages = _fetch_packagist_packages(package_name)
663
+ pkg = _find_packagist_package_entry(packages, oss_version)
664
+ if not pkg:
665
+ logger.warning(f'Cannot find packagist package entry ({package_name}:{oss_version or "latest"})')
666
+ return False, ''
667
+
668
+ dist = pkg.get('dist') or {}
669
+ dist_url = (dist.get('url') or '').strip()
670
+ if dist_url:
671
+ return True, dist_url
672
+
673
+ source_archive = _archive_url_from_packagist_source(pkg.get('source') or {})
674
+ if source_archive:
675
+ return True, source_archive
676
+
677
+ logger.warning(
678
+ f'No dist/source archive for packagist package ({package_name}:{pkg.get("version", "")})'
679
+ )
680
+ except Exception as error:
681
+ logger.warning(f'Cannot find the link for packagist (url:{link}) {error}')
682
+
683
+ return ret, new_link
684
+
685
+
552
686
  def get_download_location_for_cargo(link):
553
687
  # get the url for downloading source file: https://crates.io/api/v1/crates/<name>/<version>/download
554
688
  ret = False
@@ -41,6 +41,7 @@ SHEET_NAME_FOR_SCANNER = {
41
41
  # Cocoapods : https://cocoapods.org/(package)
42
42
  # go : https://pkg.go.dev/(package_name_with_slash)@(version)
43
43
  # cargo : https://crates.io/crates/(crate_name)/(version)
44
+ # packagist : https://packagist.org/packages/(vendor)/(package)[#v(version)|/(version)]
44
45
  PKG_PATTERN = {
45
46
  "pypi": r'https?:\/\/pypi\.org\/project\/([^\/]+)[\/]?([^\/]*)',
46
47
  "pypi2": r'https?:\/\/files\.pythonhosted\.org\/packages\/source\/[\w]\/([^\/]+)\/[\S]+-([^\-]+)\.tar\.gz',
@@ -53,4 +54,5 @@ PKG_PATTERN = {
53
54
  "cocoapods": r'https?:\/\/cocoapods\.org\/pods\/([^\/]+)',
54
55
  "go": r'https?:\/\/pkg.go.dev\/([^\@]+)\@?v?([^\/]*)',
55
56
  "cargo": r'https?:\/\/crates\.io\/crates\/([^\/]+)\/?([^\/]*)',
57
+ "packagist": r'https?:\/\/packagist\.org\/packages\/([^\/]+\/[^\/#?]+)(?:\/([^\/#?]+))?(?:\#v?([^\/#?]*))?',
56
58
  }
@@ -83,6 +83,10 @@ _HELP_MESSAGE_DOWNLOAD = """
83
83
 
84
84
  # Download and unzip a compressed file
85
85
  fosslight_download -s https://example.com/archive.zip -z -t output_dir
86
+
87
+ # Download a Packagist (PHP/Composer) package
88
+ fosslight_download -s https://packagist.org/packages/mustache/mustache -t output_dir
89
+ fosslight_download -s https://packagist.org/packages/mustache/mustache -c v2.14.2 -t output_dir
86
90
  """
87
91
 
88
92
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_util
3
- Version: 2.2.2
3
+ Version: 2.2.3
4
4
  Summary: FOSSLight Util
5
5
  Author: LG Electronics
6
6
  License-Expression: Apache-2.0
@@ -39,6 +39,7 @@ src/fosslight_util/resources/licenses.json
39
39
  tests/test_cyclonedx.py
40
40
  tests/test_decide_checkout.py
41
41
  tests/test_download.py
42
+ tests/test_download_packagist.py
42
43
  tests/test_download_version_hint.py
43
44
  tests/test_opossum.py
44
45
  tests/test_spdx_licenses.py
@@ -0,0 +1,175 @@
1
+ # Copyright (c) 2026 LG Electronics Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Tests for Packagist (PHP/Composer) download URL resolution."""
4
+
5
+ import os
6
+
7
+ import pytest
8
+
9
+ from fosslight_util import _get_downloadable_url as downloadable_url
10
+ from fosslight_util.download import cli_download_and_extract
11
+ from tests import constants
12
+
13
+
14
+ _MUSTACHE_PACKAGES = [
15
+ {
16
+ "version": "v3.2.0",
17
+ "version_normalized": "3.2.0.0",
18
+ "dist": {
19
+ "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/abc123",
20
+ "type": "zip",
21
+ "reference": "abc123",
22
+ },
23
+ "source": {
24
+ "url": "https://github.com/bobthecow/mustache.php.git",
25
+ "type": "git",
26
+ "reference": "abc123",
27
+ },
28
+ },
29
+ {
30
+ "version": "v2.14.2",
31
+ "version_normalized": "2.14.2.0",
32
+ "dist": {
33
+ "url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/def456",
34
+ "type": "zip",
35
+ "reference": "def456",
36
+ },
37
+ "source": {
38
+ "url": "https://github.com/bobthecow/mustache.php.git",
39
+ "type": "git",
40
+ "reference": "def456",
41
+ },
42
+ },
43
+ ]
44
+
45
+
46
+ class _FakeJsonResponse:
47
+ def __init__(self, payload, status_code=200):
48
+ self._payload = payload
49
+ self.status_code = status_code
50
+
51
+ def json(self):
52
+ return self._payload
53
+
54
+
55
+ def test_packagist_pattern_extracts_name_and_fragment_version(monkeypatch):
56
+ monkeypatch.setattr(
57
+ downloadable_url, "_fetch_packagist_packages", lambda _: _MUSTACHE_PACKAGES
58
+ )
59
+ name, version, link, pkg_type = downloadable_url.extract_name_version_from_link(
60
+ "https://packagist.org/packages/mustache/mustache#v2.14.2",
61
+ "",
62
+ )
63
+ assert pkg_type == "packagist"
64
+ assert name == "packagist:mustache/mustache"
65
+ assert version in ("2.14.2", "v2.14.2")
66
+ assert "mustache/mustache" in link
67
+
68
+
69
+ def test_find_packagist_package_entry_matches_with_or_without_v():
70
+ pkg = downloadable_url._find_packagist_package_entry(_MUSTACHE_PACKAGES, "2.14.2")
71
+ assert pkg is not None
72
+ assert pkg["version"] == "v2.14.2"
73
+
74
+ pkg = downloadable_url._find_packagist_package_entry(_MUSTACHE_PACKAGES, "v2.14.2")
75
+ assert pkg is not None
76
+ assert pkg["version"] == "v2.14.2"
77
+
78
+ latest = downloadable_url._find_packagist_package_entry(_MUSTACHE_PACKAGES, "")
79
+ assert latest["version"] == "v3.2.0"
80
+
81
+
82
+ def test_archive_url_from_packagist_source_github():
83
+ url = downloadable_url._archive_url_from_packagist_source(
84
+ {
85
+ "type": "git",
86
+ "url": "https://github.com/bobthecow/mustache.php.git",
87
+ "reference": "deadbeef",
88
+ }
89
+ )
90
+ assert url == "https://github.com/bobthecow/mustache.php/archive/deadbeef.zip"
91
+
92
+
93
+ def test_get_download_location_for_packagist_uses_dist_url(monkeypatch):
94
+ def fake_fetch(package_name):
95
+ assert package_name == "mustache/mustache"
96
+ return _MUSTACHE_PACKAGES
97
+
98
+ monkeypatch.setattr(downloadable_url, "_fetch_packagist_packages", fake_fetch)
99
+
100
+ ok, link = downloadable_url.get_download_location_for_packagist(
101
+ "packagist.org/packages/mustache/mustache/v2.14.2"
102
+ )
103
+ assert ok is True
104
+ assert link == "https://api.github.com/repos/bobthecow/mustache.php/zipball/def456"
105
+
106
+
107
+ def test_get_download_location_for_packagist_falls_back_to_source_archive(monkeypatch):
108
+ packages = [
109
+ {
110
+ "version": "v1.0.0",
111
+ "version_normalized": "1.0.0.0",
112
+ "dist": {},
113
+ "source": {
114
+ "type": "git",
115
+ "url": "https://github.com/acme/demo.git",
116
+ "reference": "111aaa",
117
+ },
118
+ }
119
+ ]
120
+ monkeypatch.setattr(downloadable_url, "_fetch_packagist_packages", lambda _: packages)
121
+
122
+ ok, link = downloadable_url.get_download_location_for_packagist(
123
+ "packagist.org/packages/acme/demo/v1.0.0"
124
+ )
125
+ assert ok is True
126
+ assert link == "https://github.com/acme/demo/archive/111aaa.zip"
127
+
128
+
129
+ def test_get_downloadable_url_packagist_latest(monkeypatch):
130
+ monkeypatch.setattr(
131
+ downloadable_url, "_fetch_packagist_packages", lambda _: _MUSTACHE_PACKAGES
132
+ )
133
+
134
+ ok, link, oss_name, oss_version, pkg_type = downloadable_url.get_downloadable_url(
135
+ "https://packagist.org/packages/mustache/mustache",
136
+ "",
137
+ )
138
+ assert ok is True
139
+ assert pkg_type == "packagist"
140
+ assert oss_name == "packagist:mustache/mustache"
141
+ assert oss_version == "v3.2.0"
142
+ assert link.endswith("/zipball/abc123")
143
+
144
+
145
+ def test_get_downloadable_url_packagist_checkout_version(monkeypatch):
146
+ monkeypatch.setattr(
147
+ downloadable_url, "_fetch_packagist_packages", lambda _: _MUSTACHE_PACKAGES
148
+ )
149
+
150
+ ok, link, oss_name, oss_version, pkg_type = downloadable_url.get_downloadable_url(
151
+ "https://packagist.org/packages/mustache/mustache",
152
+ "v2.14.2",
153
+ )
154
+ assert ok is True
155
+ assert pkg_type == "packagist"
156
+ assert oss_name == "packagist:mustache/mustache"
157
+ assert oss_version == "v2.14.2"
158
+ assert link.endswith("/zipball/def456")
159
+
160
+
161
+ @pytest.mark.parametrize(
162
+ "project_url",
163
+ [
164
+ "https://packagist.org/packages/mustache/mustache",
165
+ "https://packagist.org/packages/mustache/mustache#v2.14.2",
166
+ ],
167
+ )
168
+ def test_download_packagist_live(project_url):
169
+ target_dir = os.path.join(constants.TEST_RESULT_DIR, "download/packagist_mustache")
170
+ log_dir = os.path.join(constants.TEST_RESULT_DIR, "download_log/packagist_mustache")
171
+
172
+ success, _, _, _, _ = cli_download_and_extract(project_url, target_dir, log_dir)
173
+
174
+ assert success is True
175
+ assert len(os.listdir(target_dir)) > 0
File without changes
File without changes
File without changes