fosslight-util 2.2.0__tar.gz → 2.2.2__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 (50) hide show
  1. {fosslight_util-2.2.0/src/fosslight_util.egg-info → fosslight_util-2.2.2}/PKG-INFO +1 -1
  2. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/pyproject.toml +1 -1
  3. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/cover.py +29 -8
  4. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/download.py +48 -1
  5. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/oss_item.py +6 -0
  6. fosslight_util-2.2.2/src/fosslight_util/time.py +60 -0
  7. {fosslight_util-2.2.0 → fosslight_util-2.2.2/src/fosslight_util.egg-info}/PKG-INFO +1 -1
  8. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util.egg-info/SOURCES.txt +2 -0
  9. fosslight_util-2.2.2/tests/test_decide_checkout.py +69 -0
  10. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/LICENSE +0 -0
  11. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/MANIFEST.in +0 -0
  12. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/README.md +0 -0
  13. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/setup.cfg +0 -0
  14. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/__init__.py +0 -0
  15. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/_get_downloadable_url.py +0 -0
  16. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/compare_yaml.py +0 -0
  17. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/constant.py +0 -0
  18. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/correct.py +0 -0
  19. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/exclude.py +0 -0
  20. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/get_pom_license.py +0 -0
  21. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/help.py +0 -0
  22. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/output_format.py +0 -0
  23. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/parsing_yaml.py +0 -0
  24. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/read_excel.py +0 -0
  25. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/resources/frequentLicenselist.json +0 -0
  26. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/resources/frequent_license_nick_list.json +0 -0
  27. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/resources/licenses.json +0 -0
  28. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/set_log.py +0 -0
  29. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/spdx_licenses.py +0 -0
  30. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/timer_thread.py +0 -0
  31. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_cyclonedx.py +0 -0
  32. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_excel.py +0 -0
  33. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_opossum.py +0 -0
  34. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_scancodejson.py +0 -0
  35. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_spdx.py +0 -0
  36. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_txt.py +0 -0
  37. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util/write_yaml.py +0 -0
  38. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util.egg-info/dependency_links.txt +0 -0
  39. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util.egg-info/entry_points.txt +0 -0
  40. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util.egg-info/requires.txt +0 -0
  41. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/src/fosslight_util.egg-info/top_level.txt +0 -0
  42. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_cyclonedx.py +0 -0
  43. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_download.py +0 -0
  44. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_download_version_hint.py +0 -0
  45. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_opossum.py +0 -0
  46. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_spdx_licenses.py +0 -0
  47. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_text.py +0 -0
  48. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_write_excel_hidden_sheet.py +0 -0
  49. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/tests/test_write_output.py +0 -0
  50. {fosslight_util-2.2.0 → fosslight_util-2.2.2}/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.0
3
+ Version: 2.2.2
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.0"
10
+ version = "2.2.2"
11
11
  description = "FOSSLight Util"
12
12
  readme = "README.md"
13
13
  license = "Apache-2.0"
@@ -4,13 +4,22 @@
4
4
  # SPDX-License-Identifier: Apache-2.0
5
5
 
6
6
  import os
7
+ import re
7
8
  import sys
9
+ import yaml
8
10
  from fosslight_util.help import print_package_version
11
+ from fosslight_util.time import format_display_time, format_running_time
12
+
13
+
14
+ def dump_result_log(result_log):
15
+ log_text = yaml.safe_dump(result_log, allow_unicode=True, sort_keys=True)
16
+ log_text = re.sub(r"Running time: ['\"](.+?)['\"]\n", r"Running time: \1\n", log_text)
17
+ return log_text.strip()
9
18
 
10
19
 
11
20
  class CoverItem:
12
21
  tool_name_key = "Tool information"
13
- start_time_key = "Start time"
22
+ start_time_key = "Running time"
14
23
  python_ver_key = "Python version"
15
24
  analyzed_path_key = "Analyzed path"
16
25
  excluded_path_key = "Excluded path"
@@ -23,7 +32,8 @@ class CoverItem:
23
32
  "fosslight_binary"
24
33
  ]
25
34
 
26
- def __init__(self, tool_name="", start_time="", input_path="", comment="", exclude_path=[], simple_mode=True):
35
+ def __init__(self, tool_name="", start_time="", input_path="", comment="", exclude_path=[], simple_mode=True,
36
+ finish_time=""):
27
37
  if simple_mode:
28
38
  self.tool_name = f'{tool_name} v{print_package_version(tool_name, "", False)}'
29
39
  else:
@@ -34,11 +44,9 @@ class CoverItem:
34
44
  ])
35
45
  self.tool_name = f'{first_pkg} ({remaining_pkgs})'
36
46
 
37
- if start_time:
38
- date, time = start_time.split('_')
39
- self.start_time = f'{date}, {time[0:2]}:{time[2:4]}'
40
- else:
41
- self.start_time = ""
47
+ self.start_time = start_time
48
+ self.finish_time = finish_time
49
+ self.running_time = self._format_running_time()
42
50
  self.input_path = os.path.abspath(input_path)
43
51
  self.exclude_path = ", ".join(exclude_path)
44
52
  self.comment = comment
@@ -57,6 +65,19 @@ class CoverItem:
57
65
  def __lt__(self, other):
58
66
  return self.get_sort_order() < other.get_sort_order()
59
67
 
68
+ def set_finish_time(self, finish_time):
69
+ self.finish_time = finish_time
70
+ self.running_time = self._format_running_time()
71
+
72
+ def _format_running_time(self):
73
+ if not self.start_time:
74
+ return ""
75
+
76
+ if not self.finish_time:
77
+ return format_display_time(self.start_time)
78
+
79
+ return format_running_time(self.start_time, self.finish_time)
80
+
60
81
  def create_merged_comment(self, cover_items):
61
82
  if not cover_items:
62
83
  return ""
@@ -69,7 +90,7 @@ class CoverItem:
69
90
  def get_print_json(self):
70
91
  json_item = {}
71
92
  json_item[self.tool_name_key] = self.tool_name
72
- json_item[self.start_time_key] = self.start_time
93
+ json_item[self.start_time_key] = self.running_time
73
94
  json_item[self.python_ver_key] = self.python_version
74
95
  json_item[self.analyzed_path_key] = self.input_path
75
96
  json_item[self.excluded_path_key] = self.exclude_path
@@ -319,7 +319,7 @@ _BASE_SEMVER_FOR_CHECKOUT = re.compile(
319
319
  re.IGNORECASE,
320
320
  )
321
321
  _SEMVER_IN_REF = re.compile(
322
- r'(?:^v\.? ?|[-_])'
322
+ r'(?:^v\.? ?|[-_]v\.? ?|[-_])'
323
323
  r'(\d+)\.(\d+)\.(\d+)'
324
324
  r'(?:(?:-[0-9A-Za-z.-]+)|(?:\.[A-Za-z][0-9A-Za-z.-]*))?'
325
325
  r'(?:\+[0-9A-Za-z.-]+)?'
@@ -467,6 +467,48 @@ def _strip_leading_v_prefix(s: str) -> str:
467
467
  return re.sub(r'^(?:v\.? ?)?', '', s.strip(), flags=re.IGNORECASE)
468
468
 
469
469
 
470
+ def _repo_name_from_git_url(git_url: str) -> str:
471
+ """Extract repository name from common git HTTPS/SSH URLs."""
472
+ if not git_url:
473
+ return ""
474
+ url = git_url.strip().rstrip('/')
475
+ if url.endswith('.git'):
476
+ url = url[:-4]
477
+ m = re.match(r'git@[^:]+:[^/]+/([^/@?#]+)$', url)
478
+ if m:
479
+ return m.group(1)
480
+ path = urllib.parse.urlparse(url).path.strip('/')
481
+ if path:
482
+ segments = path.split('/')
483
+ if len(segments) >= 2:
484
+ return segments[-1]
485
+ return ""
486
+
487
+
488
+ def _repo_prefixed_version_refs(repo_name: str, base: str) -> List[str]:
489
+ """Build {repo}-v{version} style tag candidates from checkout hint."""
490
+ if not repo_name or not base:
491
+ return []
492
+ base = base.strip()
493
+ core = _strip_leading_v_prefix(base)
494
+ if not core:
495
+ return []
496
+ repo = repo_name.strip()
497
+ candidates = [
498
+ f"{repo}-v{core}",
499
+ f"{repo}-v.{core}",
500
+ f"{repo}_{core}",
501
+ f"{repo}-{core}",
502
+ ]
503
+ seen = set()
504
+ result = []
505
+ for candidate in candidates:
506
+ if candidate not in seen:
507
+ seen.add(candidate)
508
+ result.append(candidate)
509
+ return result
510
+
511
+
470
512
  def _match_exact_or_v_prefix_ref(base: str, ref_set: set) -> Optional[str]:
471
513
  """Exact ref match, or ref equal to base after optional leading v/v./v."""
472
514
  if base in ref_set:
@@ -558,11 +600,16 @@ def decide_checkout(checkout_to="", tag="", branch="", git_url=""):
558
600
  tag_set = set(ref_dict.get("tags", []))
559
601
  branch_set = set(ref_dict.get("branches", []))
560
602
  ref_set = tag_set | branch_set
603
+ repo_name = _repo_name_from_git_url(git_url)
561
604
 
562
605
  for raw in (tag, branch, checkout_to):
563
606
  b = (raw or "").strip()
564
607
  if not b:
565
608
  continue
609
+ for candidate in _repo_prefixed_version_refs(repo_name, b):
610
+ ref, clar = _try_resolve_checkout_base(candidate, ref_set)
611
+ if ref is not None:
612
+ return ref, clar
566
613
  ref, clar = _try_resolve_checkout_base(b, ref_set)
567
614
  if ref is not None:
568
615
  return ref, clar
@@ -8,6 +8,7 @@ import os
8
8
  import hashlib
9
9
  from fosslight_util.constant import LOGGER_NAME, FOSSLIGHT_SCANNER, COMMENT_DELIMITER
10
10
  from fosslight_util.cover import CoverItem
11
+ from fosslight_util.time import current_timestamp_utc
11
12
  from typing import List, Dict
12
13
 
13
14
  _logger = logging.getLogger(LOGGER_NAME)
@@ -211,6 +212,11 @@ class ScannerItem:
211
212
  else:
212
213
  self.cover.comment = value
213
214
 
215
+ def set_cover_finish_time(self, finish_time=""):
216
+ if not finish_time:
217
+ finish_time = current_timestamp_utc()
218
+ self.cover.set_finish_time(finish_time)
219
+
214
220
  def get_cover_comment(self):
215
221
  return [item.strip() for item in self.cover.comment.split(COMMENT_DELIMITER)]
216
222
 
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2024 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ from datetime import datetime, timezone, timedelta
7
+
8
+ _TIMESTAMP_FMT = '%Y%m%d_%H%M%S'
9
+ _DISPLAY_TZ = timezone(timedelta(hours=9)) # KST (UTC+9)
10
+
11
+
12
+ def current_timestamp_utc() -> str:
13
+ """Return current time as UTC wall-clock string for internal storage."""
14
+ return datetime.now(timezone.utc).strftime(_TIMESTAMP_FMT)
15
+
16
+
17
+ def timestamp_for_filename(utc_timestamp: str) -> str:
18
+ """Convert UTC storage timestamp to KST for file and directory names."""
19
+ if not utc_timestamp:
20
+ return current_timestamp_for_filename()
21
+ return _parse_utc_timestamp(utc_timestamp).astimezone(_DISPLAY_TZ).strftime(_TIMESTAMP_FMT)
22
+
23
+
24
+ def current_timestamp_for_filename() -> str:
25
+ """Return current time as KST wall-clock string for file and directory names."""
26
+ return timestamp_for_filename(current_timestamp_utc())
27
+
28
+
29
+ def _parse_utc_timestamp(value: str) -> datetime:
30
+ return datetime.strptime(value, _TIMESTAMP_FMT).replace(tzinfo=timezone.utc)
31
+
32
+
33
+ def format_display_time(value: str) -> str:
34
+ return _parse_utc_timestamp(value).astimezone(_DISPLAY_TZ).strftime('%Y%m%d_%H:%M:%S')
35
+
36
+
37
+ def _format_duration_compact(total_seconds: int) -> str:
38
+ total_seconds = max(0, total_seconds)
39
+ hours, remaining_seconds = divmod(total_seconds, 3600)
40
+ minutes, seconds = divmod(remaining_seconds, 60)
41
+
42
+ duration_items = []
43
+ if hours > 0:
44
+ duration_items.append(f'{hours}h')
45
+ if minutes > 0:
46
+ duration_items.append(f'{minutes}m')
47
+ duration_items.append(f'{seconds}s')
48
+ return f'({" ".join(duration_items)})'
49
+
50
+
51
+ def format_running_time(start_time: str, finish_time: str) -> str:
52
+ start_utc = _parse_utc_timestamp(start_time)
53
+ finish_utc = _parse_utc_timestamp(finish_time)
54
+ total_seconds = int((finish_utc - start_utc).total_seconds())
55
+
56
+ start_display = start_utc.astimezone(_DISPLAY_TZ).strftime('%Y%m%d_%H:%M:%S')
57
+ finish_display = finish_utc.astimezone(_DISPLAY_TZ).strftime('%Y%m%d_%H:%M:%S')
58
+ duration = _format_duration_compact(total_seconds)
59
+
60
+ return f'{start_display} ~ {finish_display} {duration}'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_util
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: FOSSLight Util
5
5
  Author: LG Electronics
6
6
  License-Expression: Apache-2.0
@@ -18,6 +18,7 @@ src/fosslight_util/parsing_yaml.py
18
18
  src/fosslight_util/read_excel.py
19
19
  src/fosslight_util/set_log.py
20
20
  src/fosslight_util/spdx_licenses.py
21
+ src/fosslight_util/time.py
21
22
  src/fosslight_util/timer_thread.py
22
23
  src/fosslight_util/write_cyclonedx.py
23
24
  src/fosslight_util/write_excel.py
@@ -36,6 +37,7 @@ src/fosslight_util/resources/frequentLicenselist.json
36
37
  src/fosslight_util/resources/frequent_license_nick_list.json
37
38
  src/fosslight_util/resources/licenses.json
38
39
  tests/test_cyclonedx.py
40
+ tests/test_decide_checkout.py
39
41
  tests/test_download.py
40
42
  tests/test_download_version_hint.py
41
43
  tests/test_opossum.py
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2026 LG Electronics Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Tests for git checkout ref resolution (decide_checkout)."""
4
+
5
+ import pytest
6
+
7
+ from fosslight_util.download import (
8
+ _repo_name_from_git_url,
9
+ _repo_prefixed_version_refs,
10
+ _try_resolve_checkout_base,
11
+ clarified_version_from_oss_version,
12
+ decide_checkout,
13
+ )
14
+
15
+
16
+ @pytest.mark.parametrize(
17
+ "git_url,expected",
18
+ [
19
+ ("https://github.com/rrousselGit/freezed", "freezed"),
20
+ ("https://github.com/rrousselGit/freezed.git", "freezed"),
21
+ ("git@github.com:rrousselGit/freezed.git", "freezed"),
22
+ ],
23
+ )
24
+ def test_repo_name_from_git_url(git_url, expected):
25
+ assert _repo_name_from_git_url(git_url) == expected
26
+
27
+
28
+ def test_repo_prefixed_version_refs():
29
+ assert _repo_prefixed_version_refs("freezed", "2.4.4") == [
30
+ "freezed-v2.4.4",
31
+ "freezed-v.2.4.4",
32
+ "freezed_2.4.4",
33
+ "freezed-2.4.4",
34
+ ]
35
+ assert _repo_prefixed_version_refs("freezed", "v2.4.4")[0] == "freezed-v2.4.4"
36
+
37
+
38
+ def test_try_resolve_checkout_base_matches_repo_prefixed_tag():
39
+ ref_set = {"freezed-v2.4.4", "freezed_annotation-v2.4.4", "master"}
40
+ ref, clar = _try_resolve_checkout_base("freezed-v2.4.4", ref_set)
41
+ assert ref == "freezed-v2.4.4"
42
+ assert clar == "2.4.4"
43
+
44
+
45
+ def test_try_resolve_checkout_base_semver_matches_repo_prefixed_tag():
46
+ ref_set = {"freezed-v2.4.4", "freezed_annotation-v2.4.4", "master"}
47
+ ref, clar = _try_resolve_checkout_base("2.4.4", ref_set)
48
+ assert ref == "freezed-v2.4.4"
49
+ assert clar == "2.4.4"
50
+
51
+
52
+ def test_clarified_version_from_repo_prefixed_tag():
53
+ assert clarified_version_from_oss_version("freezed-v2.4.4") == "2.4.4"
54
+
55
+
56
+ def test_decide_checkout_resolves_repo_prefixed_tag(monkeypatch):
57
+ tags = ["freezed-v2.4.4", "freezed_annotation-v2.4.4"]
58
+ monkeypatch.setattr(
59
+ "fosslight_util.download.get_remote_refs",
60
+ lambda _url: {"tags": tags, "branches": ["master"]},
61
+ )
62
+
63
+ ref, clar = decide_checkout(
64
+ checkout_to="2.4.4",
65
+ git_url="https://github.com/rrousselGit/freezed",
66
+ )
67
+
68
+ assert ref == "freezed-v2.4.4"
69
+ assert clar == "2.4.4"
File without changes
File without changes
File without changes