blob: 4ce44bc2318f3d6b48fa0acad0d1c967789bc796 [file] [log] [blame]
#!/usr/bin/env python3
import argparse
import os
import pathlib
import requests
import subprocess
import xml.etree.ElementTree as ET
import yaml
from concurrent.futures import ThreadPoolExecutor
from functools import partial
BASE_REPO_URL = "https://repo.eclipse.org/content/groups/releases"
JGIT_GROUP_ID = "org.eclipse.jgit"
JGIT_PARENT_ARTIFACT_ID = "org.eclipse.jgit-parent"
DOWNLOAD_DIR = "staging-deploy"
class Dumper(yaml.Dumper):
def increase_indent(self, flow=False, *args, **kwargs):
return super().increase_indent(flow=flow, indentless=False)
def fetch_and_parse_pom(url):
"""Fetches and parses a Maven POM file from a URL."""
print(f"Fetching parent POM from {url}")
try:
response = requests.get(url)
response.raise_for_status()
return ET.fromstring(response.content)
except requests.exceptions.RequestException as e:
print(f"Error fetching POM: {e}")
exit(1)
def get_pom_info(pom_root):
"""Extracts GAV and modules from a parsed POM XML."""
ns = {"m": "http://maven.apache.org/POM/4.0.0"}
group_id_elem = pom_root.find("m:groupId", ns)
if group_id_elem is None:
group_id_elem = pom_root.find("m:parent/m:groupId", ns)
version_elem = pom_root.find("m:version", ns)
if version_elem is None:
version_elem = pom_root.find("m:parent/m:version", ns)
java_version_elem = None
for prop in pom_root.findall("m:properties", ns):
java_version_elem = prop.find("m:java.version", ns)
if java_version_elem is not None:
break
info = {
"groupId": group_id_elem.text,
"artifactId": pom_root.find("m:artifactId", ns).text,
"version": version_elem.text,
"java_version": java_version_elem.text,
"modules": [
module.text for module in pom_root.findall("m:modules/m:module", ns)
],
}
return info
def download_artifacts(artifacts_to_download):
"""Downloads all artifacts for the given GAVs into the DOWNLOAD_DIR."""
for artifact in artifacts_to_download:
group_path = artifact["groupId"].replace(".", "/")
base_url = f"{BASE_REPO_URL}/{group_path}/{artifact['artifactId']}/{artifact['version']}"
group_download_dir = f"{DOWNLOAD_DIR}/{group_path}"
artifact_download_dir = (
f"{group_download_dir}/{artifact['artifactId']}/{artifact['version']}"
)
if not os.path.exists(artifact_download_dir):
os.makedirs(artifact_download_dir)
filenames = [f"{artifact['artifactId']}-{artifact['version']}.pom"]
if "parent" not in artifact["artifactId"]:
filenames.extend(
[
f"{artifact['artifactId']}-{artifact['version']}.jar",
f"{artifact['artifactId']}-{artifact['version']}-sources.jar",
f"{artifact['artifactId']}-{artifact['version']}-javadoc.jar",
f"{artifact['artifactId']}-{artifact['version']}-cyclonedx.json",
f"{artifact['artifactId']}-{artifact['version']}.sh",
]
)
print(
f"\nDownloading artifacts for {artifact['groupId']}:{artifact['artifactId']}:{artifact['version']}"
)
download_task = partial(download_file, artifact_download_dir, base_url)
with ThreadPoolExecutor(max_workers=8) as executor:
executor.map(download_task, filenames)
def download_file(artifact_download_dir, base_url, filename):
"""Downloads the given file from base_url to artifact_download_dir in a Maven repository layout"""
url = f"{base_url}/{filename}"
local_path = os.path.join(artifact_download_dir, filename)
try:
with requests.get(url, stream=True) as r:
if r.status_code == 200:
with open(local_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
print(f" Downloaded: {filename}")
elif r.status_code == 404:
pass
else:
print(
f" Warning: Failed to download {filename} (Status: {r.status_code})"
)
except requests.exceptions.RequestException as e:
print(f" Error downloading {filename}: {e}")
def sign_artifacts(passphrase_file):
"""GPG signs all artifacts under DOWNLOAD_DIR"""
artifact_dir = pathlib.Path(DOWNLOAD_DIR)
print("Signing artifacts")
for file in artifact_dir.rglob("*"):
if file.is_file() and file.suffix in {".jar", ".pom", ".sh", ".json"}:
try:
subprocess.run(
[
"gpg",
"--batch",
"--yes",
"--pinentry-mode",
"loopback",
"--passphrase-file",
passphrase_file,
"--armor",
"--detach-sign",
str(file),
],
check=True,
)
print(f" Signed: {str(file)}")
except subprocess.CalledProcessError as e:
print(f"Error signing {file}: {e.stderr}")
def jreleaser_set_values(jgit_version, java_version):
"""Generates jreleaser.yml from template and sets values extracted from parent pom"""
if not jgit_version:
print("jgit_version undefined")
if not java_version:
print("java_version undefined")
with open("jreleaser.yml.template", "r") as f:
data = yaml.safe_load(f)
data["project"]["version"] = jgit_version
data["project"]["languages"]["java"]["version"] = java_version
with open("jreleaser.yml", "w") as f:
yaml.dump(data, f, sort_keys=False, Dumper=Dumper)
def main():
"""Main execution function."""
parser = argparse.ArgumentParser(
description="""Download artifacts of a JGit release from repo.eclipse.org.""",
usage="%(prog)s <version> [--passphrase_file <path to passphrase file> (default: ~/.gnupg/passphrase)]",
)
# 'version' as a positional argument
parser.add_argument(
"version", help="The JGit version to process (e.g., 5.13.4.202507202350-r)."
)
parser.add_argument(
"--passphrase_file",
help="Path to file containing gpg passphrase",
default=f"{pathlib.Path.home()}/.gnupg/passphrase",
)
args = parser.parse_args()
jgit_version = args.version
passphrase_file = args.passphrase_file
group_path = JGIT_GROUP_ID.replace(".", "/")
filename = f"{JGIT_PARENT_ARTIFACT_ID}-{jgit_version}.pom"
parent_pom_url = f"{BASE_REPO_URL}/{group_path}/{JGIT_PARENT_ARTIFACT_ID}/{jgit_version}/{filename}"
pom_root = fetch_and_parse_pom(parent_pom_url)
parent_info = get_pom_info(pom_root)
if parent_info["version"] != jgit_version:
print(
f"Warning: The version in the POM ('{parent_info['version']}') does not match the requested version ('{jgit_version}')."
)
print("Proceeding with the version found in the POM file.")
print("\n--- Project Information ---")
print(f"GroupId: {parent_info['groupId']}")
print(f"ArtifactId: {parent_info['artifactId']}")
print(f"Version: {parent_info['version']}")
print(f"Found {len(parent_info['modules'])} modules.")
artifacts_to_process = []
artifacts_to_process.append(
{
"groupId": parent_info["groupId"],
"artifactId": parent_info["artifactId"],
"version": parent_info["version"],
}
)
for module in parent_info["modules"]:
if not module.endswith((".test", ".coverage", ".benchmarks")):
artifacts_to_process.append(
{
"groupId": parent_info["groupId"],
"artifactId": module,
"version": parent_info["version"],
}
)
download_artifacts(artifacts_to_process)
sign_artifacts(passphrase_file)
jreleaser_set_values(parent_info["version"], parent_info["java_version"])
if __name__ == "__main__":
main()