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.
- {fosslight_util-2.2.2/src/fosslight_util.egg-info → fosslight_util-2.2.4}/PKG-INFO +5 -1
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/README.md +4 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/pyproject.toml +1 -1
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/_get_downloadable_url.py +135 -1
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/constant.py +2 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/download.py +660 -144
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/help.py +8 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4/src/fosslight_util.egg-info}/PKG-INFO +5 -1
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/SOURCES.txt +1 -0
- fosslight_util-2.2.4/tests/test_download_packagist.py +175 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/LICENSE +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/MANIFEST.in +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/setup.cfg +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/__init__.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/compare_yaml.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/correct.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/cover.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/exclude.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/get_pom_license.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/oss_item.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/output_format.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/parsing_yaml.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/read_excel.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/resources/frequentLicenselist.json +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/resources/frequent_license_nick_list.json +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/resources/licenses.json +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/set_log.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/spdx_licenses.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/time.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/timer_thread.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_cyclonedx.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_excel.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_opossum.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_scancodejson.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_spdx.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_txt.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util/write_yaml.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/dependency_links.txt +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/entry_points.txt +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/requires.txt +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/src/fosslight_util.egg-info/top_level.txt +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_cyclonedx.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_decide_checkout.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_download.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_download_version_hint.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_opossum.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_spdx_licenses.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_text.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_write_excel_hidden_sheet.py +0 -0
- {fosslight_util-2.2.2 → fosslight_util-2.2.4}/tests/test_write_output.py +0 -0
- {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.
|
|
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/"
|
|
@@ -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
|
}
|