Skip to content

Commit b881f0b

Browse files
committed
feat(RELEASE-2476): add Python script for collect-charon-params task
Assisted-by: Cursor Signed-off-by: Lubomir Gallovic <lgallovi@redhat.com>
1 parent a0c68a7 commit b881f0b

2 files changed

Lines changed: 740 additions & 0 deletions

File tree

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
#!/usr/bin/env python3
2+
"""Collect charon configuration from data, snapshot, and release files.
3+
4+
Extract parameters needed by charon (MRRC/NRRC publishing tool) and
5+
write them as a shell-sourceable env file, a config file, and Tekton
6+
result files.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import argparse
12+
import dataclasses
13+
import json
14+
from pathlib import Path
15+
from typing import Any
16+
17+
import tekton
18+
from file import load_json_dict
19+
from logger import logger
20+
21+
PROG = "collect_charon_params.py"
22+
23+
24+
@dataclasses.dataclass(frozen=True)
25+
class CharonParams:
26+
"""Extracted charon parameters ready for downstream tasks."""
27+
28+
target: str
29+
product_name: str
30+
product_version: str
31+
sign_key: str
32+
oci_registry: str
33+
aws_secret: str
34+
sign_ca_secret: str
35+
author: str
36+
config: Any
37+
38+
39+
def collect_charon_params(
40+
data: dict[str, Any],
41+
snapshot: dict[str, Any],
42+
release: dict[str, Any],
43+
) -> CharonParams:
44+
"""Extract charon configuration from loaded JSON dicts."""
45+
charon: dict[str, Any] = data["charon"]
46+
47+
environment: str = charon["environment"]
48+
release_val: str = charon["release"]
49+
package_type: str = charon.get("packageType", "maven")
50+
target = f"{environment}-{package_type}-{release_val}"
51+
52+
product_name: str = data["releaseNotes"]["product_name"]
53+
product_version: str = data["releaseNotes"]["product_version"]
54+
55+
signing = charon.get("signing") or {}
56+
sign_key: str = signing.get("signKey", "")
57+
sign_ca_secret: str = signing.get("signCASecret", "")
58+
59+
aws_secret: str = charon["awsSecret"]
60+
61+
components = snapshot.get("components", [])
62+
try:
63+
oci_registry = "%".join(c["containerImage"] for c in components)
64+
except KeyError:
65+
logger.error("One or more components are missing the 'containerImage' key")
66+
raise
67+
68+
author: str = release["status"]["attribution"]["author"]
69+
70+
config: Any = charon["config"]
71+
72+
return CharonParams(
73+
target=target,
74+
product_name=product_name,
75+
product_version=product_version,
76+
sign_key=sign_key,
77+
oci_registry=oci_registry,
78+
aws_secret=aws_secret,
79+
sign_ca_secret=sign_ca_secret,
80+
author=author,
81+
config=config,
82+
)
83+
84+
85+
def write_charon_env(env_path: Path, params: CharonParams) -> None:
86+
"""Write charon parameters as a shell-sourceable env file."""
87+
lines = [
88+
f"export CHARON_TARGET={params.target}",
89+
f'export CHARON_PRODUCT_NAME="{params.product_name}"',
90+
f'export CHARON_PRODUCT_VERSION="{params.product_version}"',
91+
]
92+
if params.sign_key:
93+
lines.append(f'export CHARON_SIGN_KEY="{params.sign_key}"')
94+
lines.append(f'export CHARON_OCI_REGISTRY="{params.oci_registry}"')
95+
lines.append(f'export CHARON_AUTHOR="{params.author}"')
96+
env_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
97+
98+
99+
def write_charon_config(config_path: Path, config: Any) -> None:
100+
"""Write the charon config file.
101+
102+
String values are written as-is (matching ``jq -r`` behaviour);
103+
non-string values are serialised as JSON.
104+
"""
105+
if isinstance(config, str):
106+
text = config
107+
else:
108+
text = json.dumps(config)
109+
config_path.write_text(text + "\n", encoding="utf-8")
110+
111+
112+
def run(
113+
work_dir: Path,
114+
data_json_path: str,
115+
snapshot_path: str,
116+
release_path: str,
117+
result_charon_param_file_path: Path,
118+
result_charon_config_file_path: Path,
119+
result_charon_aws_secret: Path,
120+
result_charon_sign_ca_secret: Path,
121+
) -> None:
122+
"""Orchestrate collection of charon parameters."""
123+
data = load_json_dict(work_dir / data_json_path)
124+
snapshot = load_json_dict(work_dir / snapshot_path)
125+
release_data = load_json_dict(work_dir / release_path)
126+
127+
params = collect_charon_params(data, snapshot, release_data)
128+
129+
env_rel = str(Path(data_json_path).parent / "charon.env")
130+
cfg_rel = str(Path(data_json_path).parent / "charon-config.yaml")
131+
132+
write_charon_env(work_dir / env_rel, params)
133+
write_charon_config(work_dir / cfg_rel, params.config)
134+
135+
result_charon_param_file_path.write_text(env_rel, encoding="utf-8")
136+
result_charon_config_file_path.write_text(cfg_rel, encoding="utf-8")
137+
result_charon_aws_secret.write_text(params.aws_secret, encoding="utf-8")
138+
result_charon_sign_ca_secret.write_text(params.sign_ca_secret, encoding="utf-8")
139+
140+
logger.info("Charon parameters collected successfully")
141+
142+
143+
def _parse_args(
144+
argv: list[str] | None = None,
145+
) -> argparse.Namespace:
146+
"""Parse command-line arguments."""
147+
parser = argparse.ArgumentParser(description=__doc__, prog=PROG)
148+
parser.add_argument(
149+
"--work-dir",
150+
required=True,
151+
help="Base directory for data files",
152+
)
153+
parser.add_argument(
154+
"--data-json-path",
155+
required=True,
156+
help="Relative path to the data JSON file",
157+
)
158+
parser.add_argument(
159+
"--snapshot-path",
160+
required=True,
161+
help="Relative path to the snapshot JSON file",
162+
)
163+
parser.add_argument(
164+
"--release-path",
165+
required=True,
166+
help="Relative path to the release JSON file",
167+
)
168+
return parser.parse_args(argv)
169+
170+
171+
def main(argv: list[str] | None = None) -> int:
172+
"""Parse arguments and run charon parameter collection."""
173+
args = _parse_args(argv)
174+
r_param, r_cfg, r_aws, r_ca = tekton.result_paths_from_env(
175+
"RESULT_CHARON_PARAM_FILE_PATH",
176+
"RESULT_CHARON_CONFIG_FILE_PATH",
177+
"RESULT_CHARON_AWS_SECRET",
178+
"RESULT_CHARON_SIGN_CA_SECRET",
179+
)
180+
run(
181+
work_dir=Path(args.work_dir),
182+
data_json_path=args.data_json_path,
183+
snapshot_path=args.snapshot_path,
184+
release_path=args.release_path,
185+
result_charon_param_file_path=r_param,
186+
result_charon_config_file_path=r_cfg,
187+
result_charon_aws_secret=r_aws,
188+
result_charon_sign_ca_secret=r_ca,
189+
)
190+
return 0
191+
192+
193+
if __name__ == "__main__": # pragma: no cover
194+
raise SystemExit(main())

0 commit comments

Comments
 (0)