Skip to content

Commit e51048c

Browse files
authored
Merge pull request #363 from ligangty/main
Merge from 1.3.x
2 parents 49a67e1 + 4a96f1f commit e51048c

File tree

3 files changed

+84
-57
lines changed

3 files changed

+84
-57
lines changed

charon/pkgs/maven.py

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
META_FILE_FAILED, MAVEN_METADATA_TEMPLATE,
3434
ARCHETYPE_CATALOG_TEMPLATE, ARCHETYPE_CATALOG_FILENAME,
3535
PACKAGE_TYPE_MAVEN)
36-
from typing import Dict, List, Tuple
36+
from typing import Dict, List, Tuple, Union
3737
from jinja2 import Template
3838
from datetime import datetime
3939
from zipfile import ZipFile, BadZipFile
@@ -218,7 +218,8 @@ def parse_gavs(pom_paths: List[str], root="/") -> Dict[str, Dict[str, List[str]]
218218
return gavs
219219

220220

221-
def gen_meta_file(group_id, artifact_id: str, versions: list, root="/", digest=True) -> List[str]:
221+
def gen_meta_file(group_id, artifact_id: str,
222+
versions: list, root="/", do_digest=True) -> List[str]:
222223
content = MavenMetadata(
223224
group_id, artifact_id, versions
224225
).generate_meta_file_content()
@@ -230,7 +231,7 @@ def gen_meta_file(group_id, artifact_id: str, versions: list, root="/", digest=T
230231
meta_files.append(final_meta_path)
231232
except FileNotFoundError as e:
232233
raise e
233-
if digest:
234+
if do_digest:
234235
meta_files.extend(__gen_all_digest_files(final_meta_path))
235236
return meta_files
236237

@@ -811,7 +812,7 @@ def _merge_directories_with_rename(src_dir: str, dest_dir: str, root: str):
811812
_handle_archetype_catalog_merge(src_file, dest_file)
812813
merged_count += 1
813814
logger.debug("Merged archetype catalog: %s -> %s", src_file, dest_file)
814-
if os.path.exists(dest_file):
815+
elif os.path.exists(dest_file):
815816
duplicated_count += 1
816817
logger.debug("Duplicated: %s, skipped", dest_file)
817818
else:
@@ -1332,8 +1333,8 @@ def __wildcard_metadata_paths(paths: List[str]) -> List[str]:
13321333
new_paths.append(path[:-len(".xml")] + ".*")
13331334
elif path.endswith(".md5")\
13341335
or path.endswith(".sha1")\
1335-
or path.endswith(".sha128")\
1336-
or path.endswith(".sha256"):
1336+
or path.endswith(".sha256")\
1337+
or path.endswith(".sha512"):
13371338
continue
13381339
else:
13391340
new_paths.append(path)
@@ -1342,7 +1343,7 @@ def __wildcard_metadata_paths(paths: List[str]) -> List[str]:
13421343

13431344
class VersionCompareKey:
13441345
'Used as key function for version sorting'
1345-
def __init__(self, obj):
1346+
def __init__(self, obj: str):
13461347
self.obj = obj
13471348

13481349
def __lt__(self, other):
@@ -1373,36 +1374,61 @@ def __compare(self, other) -> int:
13731374
big = max(len(xitems), len(yitems))
13741375
for i in range(big):
13751376
try:
1376-
xitem = xitems[i]
1377+
xitem: Union[str, int] = xitems[i]
13771378
except IndexError:
13781379
return -1
13791380
try:
1380-
yitem = yitems[i]
1381+
yitem: Union[str, int] = yitems[i]
13811382
except IndexError:
13821383
return 1
1383-
if xitem.isnumeric() and yitem.isnumeric():
1384+
if (isinstance(xitem, str) and isinstance(yitem, str) and
1385+
xitem.isnumeric() and yitem.isnumeric()):
13841386
xitem = int(xitem)
13851387
yitem = int(yitem)
1386-
elif xitem.isnumeric() and not yitem.isnumeric():
1388+
elif (isinstance(xitem, str) and xitem.isnumeric() and
1389+
(not isinstance(yitem, str) or not yitem.isnumeric())):
13871390
return 1
1388-
elif not xitem.isnumeric() and yitem.isnumeric():
1389-
return -1
1390-
if xitem > yitem:
1391-
return 1
1392-
elif xitem < yitem:
1391+
elif (isinstance(yitem, str) and yitem.isnumeric() and
1392+
(not isinstance(xitem, str) or not xitem.isnumeric())):
13931393
return -1
1394+
# At this point, both are the same type (both int or both str)
1395+
if isinstance(xitem, int) and isinstance(yitem, int):
1396+
if xitem > yitem:
1397+
return 1
1398+
elif xitem < yitem:
1399+
return -1
1400+
elif isinstance(xitem, str) and isinstance(yitem, str):
1401+
if xitem > yitem:
1402+
return 1
1403+
elif xitem < yitem:
1404+
return -1
13941405
else:
13951406
continue
13961407
return 0
13971408

13981409

1399-
class ArchetypeCompareKey(VersionCompareKey):
1400-
'Used as key function for GAV sorting'
1401-
def __init__(self, gav):
1402-
super().__init__(gav.version)
1410+
class ArchetypeCompareKey:
1411+
def __init__(self, gav: ArchetypeRef):
14031412
self.gav = gav
14041413

1405-
# pylint: disable=unused-private-member
1414+
def __lt__(self, other):
1415+
return self.__compare(other) < 0
1416+
1417+
def __gt__(self, other):
1418+
return self.__compare(other) > 0
1419+
1420+
def __le__(self, other):
1421+
return self.__compare(other) <= 0
1422+
1423+
def __ge__(self, other):
1424+
return self.__compare(other) >= 0
1425+
1426+
def __eq__(self, other):
1427+
return self.__compare(other) == 0
1428+
1429+
def __hash__(self):
1430+
return self.gav.__hash__()
1431+
14061432
def __compare(self, other) -> int:
14071433
x = self.gav.group_id + ":" + self.gav.artifact_id
14081434
y = other.gav.group_id + ":" + other.gav.artifact_id

charon/utils/files.py

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import os
1818
import hashlib
1919
import errno
20-
from typing import List, Tuple
20+
import tempfile
21+
import shutil
22+
from typing import List, Tuple, Optional
2123
from charon.constants import MANIFEST_SUFFIX
2224

2325

@@ -32,24 +34,37 @@ class HashType(Enum):
3234

3335
def get_hash_type(type_str: str) -> HashType:
3436
"""Get hash type from string"""
35-
if type_str.lower() == "md5":
37+
type_str_low = type_str.lower()
38+
if type_str_low == "md5":
3639
return HashType.MD5
37-
elif type_str.lower() == "sha1":
40+
elif type_str_low == "sha1":
3841
return HashType.SHA1
39-
elif type_str.lower() == "sha256":
42+
elif type_str_low == "sha256":
4043
return HashType.SHA256
41-
elif type_str.lower() == "sha512":
44+
elif type_str_low == "sha512":
4245
return HashType.SHA512
4346
else:
4447
raise ValueError("Unsupported hash type: {}".format(type_str))
4548

4649

47-
def overwrite_file(file_path: str, content: str):
48-
if not os.path.isfile(file_path):
49-
with open(file_path, mode="a", encoding="utf-8"):
50-
pass
51-
with open(file_path, mode="w", encoding="utf-8") as f:
52-
f.write(content)
50+
def overwrite_file(file_path: str, content: str) -> None:
51+
parent_dir: Optional[str] = os.path.dirname(file_path)
52+
if parent_dir:
53+
if not os.path.exists(parent_dir):
54+
os.makedirs(parent_dir, exist_ok=True)
55+
else:
56+
parent_dir = None # None explicitly means current directory for tempfile
57+
58+
# Write to temporary file first, then atomically rename
59+
fd, temp_path = tempfile.mkstemp(dir=parent_dir, text=True)
60+
try:
61+
with os.fdopen(fd, 'w', encoding='utf-8') as f:
62+
f.write(content)
63+
shutil.move(temp_path, file_path)
64+
except Exception:
65+
if os.path.exists(temp_path):
66+
os.unlink(temp_path)
67+
raise
5368

5469

5570
def read_sha1(file: str) -> str:
@@ -97,7 +112,6 @@ def digest_content(content: str, hash_type=HashType.SHA1) -> str:
97112

98113

99114
def _hash_object(hash_type: HashType):
100-
hash_obj = None
101115
if hash_type == HashType.SHA1:
102116
hash_obj = hashlib.sha1()
103117
elif hash_type == HashType.SHA256:
@@ -107,7 +121,7 @@ def _hash_object(hash_type: HashType):
107121
elif hash_type == HashType.SHA512:
108122
hash_obj = hashlib.sha512()
109123
else:
110-
raise Exception("Error: Unknown hash type for digesting.")
124+
raise ValueError("Error: Unknown hash type for digesting.")
111125
return hash_obj
112126

113127

@@ -116,14 +130,8 @@ def write_manifest(paths: List[str], root: str, product_key: str) -> Tuple[str,
116130
manifest_path = os.path.join(root, manifest_name)
117131
artifacts = []
118132
for path in paths:
119-
if path.startswith(root):
120-
path = path[len(root):]
121-
if path.startswith("/"):
122-
path = path[1:]
123-
artifacts.append(path)
124-
125-
if not os.path.isfile(manifest_path):
126-
with open(manifest_path, mode="a", encoding="utf-8"):
127-
pass
133+
rel_path = os.path.relpath(path, root)
134+
artifacts.append(rel_path)
135+
128136
overwrite_file(manifest_path, '\n'.join(artifacts))
129137
return manifest_name, manifest_path

tests/test_radas_sign_generation.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import os
2121
import json
2222
import shutil
23-
import builtins
2423
from unittest import mock
2524
from charon.utils.files import overwrite_file
2625
from charon.pkgs.radas_sign import generate_radas_sign
@@ -68,20 +67,14 @@ def test_sign_files_generation_with_failure(self):
6867
expected_asc1 = os.path.join(self.__repo_dir, "foo/bar/1.0/foo-bar-1.0.jar.asc")
6968
expected_asc2 = os.path.join(self.__repo_dir, "foo/bar/2.0/foo-bar-2.0.jar.asc")
7069

71-
# simulate expected_asc1 can not open to write properly
72-
real_open = builtins.open
73-
with mock.patch("builtins.open") as mock_open:
74-
def side_effect(path, *args, **kwargs):
75-
# this is for pylint check
76-
mode = "r"
77-
if len(args) > 0:
78-
mode = args[0]
79-
elif "mode" in kwargs:
80-
mode = kwargs["mode"]
81-
if path == expected_asc1 and "w" in mode:
70+
# simulate expected_asc1 can not be written properly
71+
real_overwrite = overwrite_file
72+
with mock.patch("charon.pkgs.radas_sign.files.overwrite_file") as mock_overwrite:
73+
def side_effect(path, content):
74+
if path == expected_asc1:
8275
raise IOError("mock write error")
83-
return real_open(path, *args, **kwargs)
84-
mock_open.side_effect = side_effect
76+
return real_overwrite(path, content)
77+
mock_overwrite.side_effect = side_effect
8578
failed, generated = generate_radas_sign(self.__repo_dir, self.__sign_result_file)
8679

8780
self.assertEqual(len(failed), 1)

0 commit comments

Comments
 (0)