fosslight-util 2.2.2__tar.gz → 2.2.4__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.4}/PKG-INFO +5 -1
  2. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/README.md +4 -0
  3. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/pyproject.toml +1 -1
  4. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/_get_downloadable_url.py +135 -1
  5. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/constant.py +2 -0
  6. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/download.py +660 -144
  7. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/help.py +8 -0
  8. {fosslight_util-2.2.2 → fosslight_util-2.2.4/src/fosslight_util.egg-info}/PKG-INFO +5 -1
  9. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/SOURCES.txt +1 -0
  10. fosslight_util-2.2.4/tests/test_download_packagist.py +175 -0
  11. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/LICENSE +0 -0
  12. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/MANIFEST.in +0 -0
  13. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/setup.cfg +0 -0
  14. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/__init__.py +0 -0
  15. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/compare_yaml.py +0 -0
  16. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/correct.py +0 -0
  17. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/cover.py +0 -0
  18. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/exclude.py +0 -0
  19. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/get_pom_license.py +0 -0
  20. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/oss_item.py +0 -0
  21. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/output_format.py +0 -0
  22. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/parsing_yaml.py +0 -0
  23. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/read_excel.py +0 -0
  24. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/resources/frequentLicenselist.json +0 -0
  25. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/resources/frequent_license_nick_list.json +0 -0
  26. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/resources/licenses.json +0 -0
  27. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/set_log.py +0 -0
  28. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/spdx_licenses.py +0 -0
  29. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/time.py +0 -0
  30. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/timer_thread.py +0 -0
  31. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_cyclonedx.py +0 -0
  32. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_excel.py +0 -0
  33. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_opossum.py +0 -0
  34. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_scancodejson.py +0 -0
  35. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_spdx.py +0 -0
  36. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_txt.py +0 -0
  37. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_yaml.py +0 -0
  38. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/dependency_links.txt +0 -0
  39. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/entry_points.txt +0 -0
  40. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/requires.txt +0 -0
  41. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/top_level.txt +0 -0
  42. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_cyclonedx.py +0 -0
  43. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_decide_checkout.py +0 -0
  44. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_download.py +0 -0
  45. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_download_version_hint.py +0 -0
  46. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_opossum.py +0 -0
  47. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_spdx_licenses.py +0 -0
  48. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_text.py +0 -0
  49. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_write_excel_hidden_sheet.py +0 -0
  50. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_write_output.py +0 -0
  51. {fosslight_util-2.2.2 → fosslight_util-2.2.4}/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.4
4
4
  Summary: FOSSLight Util
5
5
  Author: LG Electronics
6
6
  License-Expression: Apache-2.0
@@ -165,6 +165,10 @@ If you give a link, the source is downloaded to the target directory through git
165
165
  ```
166
166
  $ fosslight_download -s "https://github.com/LGE-OSS/example" -t target_dir/
167
167
  ```
168
+ Limit download size (GB) with `--size-limit` / `-l` (omit for unlimited):
169
+ ```
170
+ $ fosslight_download -s "https://github.com/LGE-OSS/example" -t target_dir/ -l 5
171
+ ```
168
172
  If you want to try with private repository, set your github token like below.
169
173
  ```
170
174
  $ fosslight_download -s "https://my_github_token@github.com/Foo/private_repo -t target_dir/"
@@ -130,6 +130,10 @@ If you give a link, the source is downloaded to the target directory through git
130
130
  ```
131
131
  $ fosslight_download -s "https://github.com/LGE-OSS/example" -t target_dir/
132
132
  ```
133
+ Limit download size (GB) with `--size-limit` / `-l` (omit for unlimited):
134
+ ```
135
+ $ fosslight_download -s "https://github.com/LGE-OSS/example" -t target_dir/ -l 5
136
+ ```
133
137
  If you want to try with private repository, set your github token like below.
134
138
  ```
135
139
  $ fosslight_download -s "https://my_github_token@github.com/Foo/private_repo -t target_dir/"
@@ -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.4"
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
  }