Skip to content

Commit cd18d27

Browse files
committed
Merge branch 'main' into release
2 parents d47286b + 97824d8 commit cd18d27

32 files changed

+767
-197
lines changed

MANIFEST.in

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
include LICENSE
2+
include README.md
3+
include pyproject.toml
4+
include setup.py
5+
recursive-include charon *.py *.json
6+
recursive-include tests *.py *.txt *.tgz *.zip *.json *.sha1
7+
exclude .github .gitignore
8+

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,14 @@ to configure AWS access credentials.
5353
### charon-upload: upload a repo to S3
5454

5555
```bash
56-
usage: charon upload $tarball --product/-p ${prod} --version/-v ${ver} [--root_path] [--ignore_patterns] [--debug] [--contain_signature] [--key]
56+
usage: charon upload $tarball [$tarball*] --product/-p ${prod} --version/-v ${ver} [--root_path] [--ignore_patterns] [--debug] [--contain_signature] [--key]
5757
```
5858

5959
This command will upload the repo in tarball to S3.
6060
It will auto-detect if the tarball is for maven or npm
6161

62+
**New in 1.3.5**: For Maven archives, this command now supports uploading multiple zip files at once. When multiple Maven zips are provided, they will be merged intelligently, including proper handling of archetype catalog files and duplicate artifact detection.
63+
6264
* For maven type, it will:
6365

6466
* Scan the tarball for all paths and collect them all.
@@ -99,11 +101,13 @@ This command will delete some paths from repo in S3.
99101
### charon-index: refresh the index.html for the specified path
100102

101103
```bash
102-
usage: charon index $PATH [-t, --target] [-D, --debug] [-q, --quiet]
104+
usage: charon index $PATH [-t, --target] [-D, --debug] [-q, --quiet] [--recursive]
103105
```
104106

105107
This command will refresh the index.html for the specified path.
106108

109+
**New in 1.3.5**: Added `--recursive` flag to support recursive indexing under the specified path.
110+
107111
* Note that if the path is a NPM metadata path which contains package.json, this refreshment will not work because this type of folder will display the package.json instead of the index.html in http request.
108112

109113
### charon-cf-check: check the invalidation status of the specified invalidation id for AWS CloudFront

charon.spec

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,24 @@ export LANG=en_US.UTF-8 LANGUAGE=en_US.en LC_ALL=en_US.UTF-8
6868
- 1.4.0 release
6969
- Add RADAS signature support
7070

71+
* Wed Oct 29 2025 Gang Li <[email protected]>
72+
- 1.3.5 release
73+
- Support recursive indexing for index function
74+
- Accept multiple maven zips for uploading
75+
7176
* Mon Jun 23 2025 Gang Li <[email protected]>
7277
- 1.3.4 release
73-
- Fix the sorting problem of index page items
78+
- Add --version flag to support version check
79+
- Bug fix: MMENG-4362 re-sort the indexing page items
80+
- Add pyproject.toml
7481

7582
* Mon Dec 16 2024 Gang Li <[email protected]>
7683
- 1.3.3 release
77-
- Fix npm del error when deleting a package which has overlapped name with others
78-
- Some code refinement
84+
- Bug fix: MMENG-4284 npm del error when deleting a package which has overlapped name with others
7985

80-
* Thu Jul 11 2024 Gang Li <[email protected]>
86+
* Wed Jul 10 2024 Gang Li <[email protected]>
8187
- 1.3.2 release
82-
- Some updates in the Containerfile.
88+
- Container file update
8389

8490
* Tue May 7 2024 Gang Li <[email protected]>
8591
- 1.3.1 release

charon/cache.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ def invalidate_paths(
8686
The default value is 3000 which is the maximum number in official doc:
8787
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#InvalidationLimits
8888
"""
89-
INPRO_W_SECS = 5
90-
NEXT_W_SECS = 1
89+
INPRO_W_SECS = 10
90+
NEXT_W_SECS = 2
9191
real_paths = [paths]
9292
# Split paths into batches by batch_size
9393
if batch_size:

charon/cmd/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from charon.cmd.cmd_checksum import init_checksum, checksum
2121
from charon.cmd.cmd_cache import init_cf, cf
2222
from charon.cmd.cmd_sign import sign
23+
from charon.cmd.cmd_merge import merge
2324

2425

2526
@group()
@@ -47,3 +48,6 @@ def cli(ctx):
4748

4849
# radas sign cmd
4950
cli.add_command(sign)
51+
52+
# maven zips merge cmd
53+
cli.add_command(merge)

charon/cmd/cmd_index.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
""",
4343
required=True
4444
)
45+
@option(
46+
"--recursive",
47+
"-r",
48+
help="If do indexing recursively under $path",
49+
is_flag=True,
50+
default=False
51+
)
4552
@option(
4653
"--config",
4754
"-c",
@@ -69,6 +76,7 @@
6976
def index(
7077
path: str,
7178
target: str,
79+
recursive: bool = False,
7280
config: str = None,
7381
debug: bool = False,
7482
quiet: bool = False,
@@ -120,7 +128,15 @@ def index(
120128
if not aws_bucket:
121129
logger.error("No bucket specified for target %s!", target)
122130
else:
123-
re_index(b, path, package_type, aws_profile, dryrun)
131+
args = {
132+
"target": b,
133+
"path": path,
134+
"package_type": package_type,
135+
"aws_profile": aws_profile,
136+
"recursive": recursive,
137+
"dry_run": dryrun
138+
}
139+
re_index(**args) # type: ignore
124140

125141
except Exception:
126142
print(traceback.format_exc())

charon/cmd/cmd_merge.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
"""
2+
Copyright (C) 2022 Red Hat, Inc. (https://github.com/Commonjava/charon)
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
from typing import List
17+
18+
from charon.utils.archive import detect_npm_archives, NpmArchiveType
19+
from charon.cmd.internal import _get_local_repos, _decide_mode
20+
from charon.pkgs.maven import _extract_tarballs
21+
from click import command, option, argument
22+
from zipfile import ZipFile, ZIP_DEFLATED
23+
from tempfile import mkdtemp
24+
25+
import logging
26+
import os
27+
import sys
28+
29+
logger = logging.getLogger(__name__)
30+
31+
32+
@argument(
33+
"repos",
34+
type=str,
35+
nargs=-1 # This allows multiple arguments for zip urls
36+
)
37+
@option(
38+
"--product",
39+
"-p",
40+
help="""
41+
The product key, will combine with version to decide
42+
the metadata of the files in tarball.
43+
""",
44+
nargs=1,
45+
required=True,
46+
multiple=False,
47+
)
48+
@option(
49+
"--version",
50+
"-v",
51+
help="""
52+
The product version, will combine with key to decide
53+
the metadata of the files in tarball.
54+
""",
55+
required=True,
56+
multiple=False,
57+
)
58+
@option(
59+
"--root_path",
60+
"-r",
61+
default="maven-repository",
62+
help="""
63+
The root path in the tarball before the real maven paths,
64+
will be trailing off before uploading.
65+
""",
66+
)
67+
@option(
68+
"--work_dir",
69+
"-w",
70+
help="""
71+
The temporary working directory into which archives should
72+
be extracted, when needed.
73+
""",
74+
)
75+
@option(
76+
"--merge_result",
77+
"-m",
78+
help="""
79+
The path of the final merged zip file will be compressed and saved.
80+
Default is the ZIP file which is created in a temporary directory based on work_dir.
81+
e.g. /tmp/work/jboss-eap-8.1.0_merged_a1b2c3/jboss-eap-8.1.0_merged.zip
82+
""",
83+
)
84+
@option(
85+
"--debug",
86+
"-D",
87+
help="Debug mode, will print all debug logs for problem tracking.",
88+
is_flag=True,
89+
default=False
90+
)
91+
@option(
92+
"--quiet",
93+
"-q",
94+
help="Quiet mode, will shrink most of the logs except warning and errors.",
95+
is_flag=True,
96+
default=False
97+
)
98+
@command()
99+
def merge(
100+
repos: List[str],
101+
product: str,
102+
version: str,
103+
root_path="maven-repository",
104+
work_dir=None,
105+
merge_result=None,
106+
debug=False,
107+
quiet=False
108+
):
109+
"""Merge multiple Maven ZIP archives and compress the result into a single ZIP file.
110+
The merged file is stored locally as specified by merge_result.
111+
112+
Note: This function does not support merging single archive, NPM archives,
113+
or archives of inconsistent types.
114+
"""
115+
_decide_mode(product, version, is_quiet=quiet, is_debug=debug)
116+
if len(repos) == 1:
117+
logger.info("Skip merge step, single archive detected, no merge needed")
118+
sys.exit(0)
119+
120+
product_key = f"{product}-{version}"
121+
archive_paths = _get_local_repos(repos)
122+
archive_types = detect_npm_archives(archive_paths)
123+
124+
maven_count = archive_types.count(NpmArchiveType.NOT_NPM)
125+
npm_count = len(archive_types) - maven_count
126+
if maven_count == len(archive_types):
127+
tmp_root = _extract_tarballs(archive_paths, root_path, product_key, dir__=work_dir)
128+
_create_merged_zip(tmp_root, merge_result, product_key, work_dir)
129+
elif npm_count == len(archive_types):
130+
logger.error("Skip merge step for the npm archives")
131+
sys.exit(1)
132+
else:
133+
logger.error("Skip merge step since the types are not consistent")
134+
sys.exit(1)
135+
136+
137+
def _create_merged_zip(
138+
root_path: str,
139+
merge_result: str,
140+
product_key: str,
141+
work_dir: str
142+
):
143+
zip_path = merge_result
144+
if not merge_result:
145+
merge_path = mkdtemp(prefix=f"{product_key}_merged_", dir=work_dir)
146+
zip_path = os.path.join(merge_path, f"{product_key}_merged.zip")
147+
148+
# pylint: disable=unused-variable
149+
with ZipFile(zip_path, 'w', ZIP_DEFLATED) as zipf:
150+
for root, dirs, files in os.walk(root_path):
151+
for file in files:
152+
file_path = os.path.join(root, file)
153+
# Calculate relative path to preserve directory structure
154+
arcname = os.path.relpath(file_path, root_path)
155+
zipf.write(file_path, arcname)
156+
logger.info("Done for the merged zip generation: %s", zip_path)

0 commit comments

Comments
 (0)