| @@ -1,14 +1,3 @@ | |||||
| maven: | |||||
| package-mirrors: | |||||
| - "https://repo.maven.apache.org/maven2" | |||||
| - "https://repo1.maven.org/maven2" | |||||
| - "https://oss.sonatype.org/content/repositories/snapshots" | |||||
| - "https://packages.confluent.io/maven" | |||||
| - "https://registry.quarkus.io/maven" | |||||
| plugin-mirrors: | |||||
| - "https://plugins.gradle.org/m2" | |||||
| configurations: | configurations: | ||||
| # Specify configurations where the packages/plugins depend on a specific kotlin and/or gradle version. | # Specify configurations where the packages/plugins depend on a specific kotlin and/or gradle version. | ||||
| - kotlin-version: ["latest", "1.8.20", "1.7.0"] | - kotlin-version: ["latest", "1.8.20", "1.7.0"] | ||||
| @@ -43,7 +43,7 @@ mkdir -p "${GRADLE_VERSIONS_DIR}" | |||||
| echo "Resolving packages and generating gradle config" | echo "Resolving packages and generating gradle config" | ||||
| python3 --version | python3 --version | ||||
| if ! python3 src/main.py --repo="repo:80" --output-dir "$PROJECTS_DIR" /package-list.yaml; then | |||||
| if ! python3 src/main.py --repo="http://repo:80/releases" --output-dir "$PROJECTS_DIR" /package-list.yaml; then | |||||
| echo "Gradle generation failed" | echo "Gradle generation failed" | ||||
| exit 255 | exit 255 | ||||
| fi | fi | ||||
| @@ -1,3 +1,3 @@ | |||||
| from .packages import Package | from .packages import Package | ||||
| from .plugins import Plugin | from .plugins import Plugin | ||||
| from .parse import Configuration, Config, MavenMirrors, parse_config | |||||
| from .parse import Configuration, parse_config | |||||
| @@ -19,19 +19,7 @@ class Configuration: | |||||
| packages: list[Package] | packages: list[Package] | ||||
| @dataclass | |||||
| class MavenMirrors: | |||||
| package_mirrors: list[str] | |||||
| plugin_mirrors: list[str] | |||||
| @dataclass | |||||
| class Config: | |||||
| configurations: list[Configuration] | |||||
| mirrors: MavenMirrors | |||||
| def parse_config(path: Path) -> Optional[Config]: | |||||
| def parse_config(path: Path) -> Optional[list[Configuration]]: | |||||
| with path.open('r') as f: | with path.open('r') as f: | ||||
| data = yaml.safe_load(f) | data = yaml.safe_load(f) | ||||
| @@ -70,13 +58,7 @@ def parse_config(path: Path) -> Optional[Config]: | |||||
| return None | return None | ||||
| else: | else: | ||||
| return Config( | |||||
| [ | |||||
| Configuration(kotlin, gradle, plugins, packages) | |||||
| for (kotlin, gradle), (plugins, packages) in configurations.items() | |||||
| ], | |||||
| MavenMirrors( | |||||
| data.get('maven', {}).get('package-mirrors', []), | |||||
| data.get('maven', {}).get('plugin-mirrors', []), | |||||
| ), | |||||
| ) | |||||
| return [ | |||||
| Configuration(kotlin, gradle, plugins, packages) | |||||
| for (kotlin, gradle), (plugins, packages) in configurations.items() | |||||
| ] | |||||
| @@ -16,7 +16,7 @@ plugins { | |||||
| """ + (""" | """ + (""" | ||||
| repositories { | repositories { | ||||
| maven { | maven { | ||||
| url=uri("http://""" + repo + """/releases") | |||||
| url=uri(\"""" + repo + """\") | |||||
| isAllowInsecureProtocol=true | isAllowInsecureProtocol=true | ||||
| } | } | ||||
| } | } | ||||
| @@ -10,7 +10,7 @@ rootProject.name = "gradle sync job" | |||||
| pluginManagement { | pluginManagement { | ||||
| repositories { | repositories { | ||||
| maven { | maven { | ||||
| url=uri("http://""" + repo + """/releases") | |||||
| url=uri(\"""" + repo + """\") | |||||
| isAllowInsecureProtocol=true | isAllowInsecureProtocol=true | ||||
| } | } | ||||
| } | } | ||||
| @@ -4,17 +4,17 @@ import dataclasses | |||||
| import logging | import logging | ||||
| from pathlib import Path | from pathlib import Path | ||||
| from config import parse_config, Configuration, MavenMirrors, Package | |||||
| from config import parse_config, Configuration, Package | |||||
| from gradle import generate_gradle | from gradle import generate_gradle | ||||
| from maven import get_effective_packages, get_effective_plugins, resolve_version | from maven import get_effective_packages, get_effective_plugins, resolve_version | ||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
| async def resolve_kotlin(configuration: Configuration, mirrors: MavenMirrors) -> list[Configuration]: | |||||
| async def resolve_kotlin(configuration: Configuration, repo: str) -> list[Configuration]: | |||||
| kotlin_versions = await resolve_version( | kotlin_versions = await resolve_version( | ||||
| Package('org.jetbrains.kotlin', 'kotlin-stdlib', configuration.kotlin_version), | Package('org.jetbrains.kotlin', 'kotlin-stdlib', configuration.kotlin_version), | ||||
| mirrors.package_mirrors, | |||||
| repo, | |||||
| ) | ) | ||||
| if not kotlin_versions: | if not kotlin_versions: | ||||
| @@ -24,13 +24,13 @@ async def resolve_kotlin(configuration: Configuration, mirrors: MavenMirrors) -> | |||||
| packages = [ | packages = [ | ||||
| resolved | resolved | ||||
| for package in configuration.packages | for package in configuration.packages | ||||
| for resolved in await get_effective_packages(package, mirrors) | |||||
| for resolved in await get_effective_packages(package, repo) | |||||
| ] | ] | ||||
| plugins = [ | plugins = [ | ||||
| resolved | resolved | ||||
| for plugin in configuration.plugins | for plugin in configuration.plugins | ||||
| for resolved in await get_effective_plugins(plugin, mirrors) | |||||
| for resolved in await get_effective_plugins(plugin, repo) | |||||
| ] | ] | ||||
| return [ | return [ | ||||
| @@ -39,25 +39,19 @@ async def resolve_kotlin(configuration: Configuration, mirrors: MavenMirrors) -> | |||||
| ] | ] | ||||
| async def main(package_list: Path, output_dir: Path, gradle_repo: str) -> None: | |||||
| config = parse_config(package_list) | |||||
| async def main(package_list: Path, output_dir: Path, repo: str) -> None: | |||||
| configurations = parse_config(package_list) | |||||
| if not config: | |||||
| print('Error in configuration') | |||||
| elif not config.configurations: | |||||
| print('No configurations defined, nothing to do.') | |||||
| elif not config.mirrors.package_mirrors and [p for c in config.configurations for p in c.packages]: | |||||
| print('No package mirrors defined. Add maven.package-mirrors in the config file.') | |||||
| elif not config.mirrors.plugin_mirrors and [p for c in config.configurations for p in c.plugins]: | |||||
| print('No plugin mirrors defined. Add maven.plugin-mirrors in the config file.') | |||||
| else: | |||||
| for i, configuration in enumerate(config.configurations): | |||||
| if configurations: | |||||
| for configuration in configurations: | |||||
| logger.info(f'Resolving configuration kotlin={configuration.kotlin_version}, gradle={configuration.gradle_version}') | logger.info(f'Resolving configuration kotlin={configuration.kotlin_version}, gradle={configuration.gradle_version}') | ||||
| try: | try: | ||||
| for c in await resolve_kotlin(configuration, config.mirrors): | |||||
| generate_gradle(c, gradle_repo, output_dir) | |||||
| for c in await resolve_kotlin(configuration, repo): | |||||
| generate_gradle(c, repo, output_dir) | |||||
| except: | except: | ||||
| logger.exception(f'Error resolving kotlin version {configuration.kotlin_version}') | logger.exception(f'Error resolving kotlin version {configuration.kotlin_version}') | ||||
| else: | |||||
| print('Error in configuration') | |||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| @@ -1,7 +1,7 @@ | |||||
| import logging | import logging | ||||
| import asyncio | |||||
| from typing import Optional | |||||
| from aiohttp import ClientSession | from aiohttp import ClientSession | ||||
| from typing import Optional, Iterable | |||||
| from config import Package | from config import Package | ||||
| from .metadata import MavenMetadata | from .metadata import MavenMetadata | ||||
| @@ -10,67 +10,29 @@ from .pom import PackagePOM | |||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
| class TooManyRequestsException(Exception): | |||||
| pass | |||||
| def group_url(group_id: str) -> str: | def group_url(group_id: str) -> str: | ||||
| return f'{group_id.replace(".", "/")}' | return f'{group_id.replace(".", "/")}' | ||||
| async def fetch_metadata(package: Package, mirrors: Iterable[str]) -> Optional[MavenMetadata]: | |||||
| async def fetch_metadata(package: Package, repo: str) -> Optional[MavenMetadata]: | |||||
| url = f'{group_url(package.group_id)}/{package.artifact_id}/maven-metadata.xml' | url = f'{group_url(package.group_id)}/{package.artifact_id}/maven-metadata.xml' | ||||
| metadata = await fetch_maven_file(url, mirrors) | |||||
| metadata = await fetch_maven_file(f'{repo}/{url}') | |||||
| return MavenMetadata(package, metadata) if metadata else None | return MavenMetadata(package, metadata) if metadata else None | ||||
| async def fetch_pom(package: Package, mirrors: Iterable[str]) -> Optional[PackagePOM]: | |||||
| async def fetch_pom(package: Package, repo) -> Optional[PackagePOM]: | |||||
| url = f'{group_url(package.group_id)}/{package.artifact_id}/{package.version}/{package.artifact_id}-{package.version}.pom' | url = f'{group_url(package.group_id)}/{package.artifact_id}/{package.version}/{package.artifact_id}-{package.version}.pom' | ||||
| pom = await fetch_maven_file(url, mirrors) | |||||
| pom = await fetch_maven_file(f'{repo}/{url}') | |||||
| return PackagePOM(package, pom) if pom else None | return PackagePOM(package, pom) if pom else None | ||||
| async def fetch_from_mirror(session: ClientSession, url: str) -> str | int: | |||||
| async with session.get(url) as response: | |||||
| if response.status == 200: | |||||
| return await response.text() | |||||
| elif response.status == 429: | |||||
| raise TooManyRequestsException() | |||||
| else: | |||||
| return response.status | |||||
| async def fetch_maven_file(url: str, mirrors: Iterable[str]) -> Optional[str]: | |||||
| async def fetch_maven_file(url: str) -> Optional[str]: | |||||
| logger.debug(f'Downloading {url}') | logger.debug(f'Downloading {url}') | ||||
| async with ClientSession() as session: | async with ClientSession() as session: | ||||
| # Retry up to 5 times | |||||
| for _ in range(5): | |||||
| # Go through the mirrors, trying to find the package. | |||||
| # If the package doesn't exist in a mirror, that mirror is removed from the list. | |||||
| # If another error occurs the mirror is kept. | |||||
| # After trying all mirrors, if the package isn't found, the remaining mirrors are retried. | |||||
| retry_mirrors = [] | |||||
| for mirror in mirrors: | |||||
| try: | |||||
| result = await fetch_from_mirror(session, f'{mirror}/{url}') | |||||
| if isinstance(result, str): | |||||
| logger.debug(f'{url} downloaded') | |||||
| return result | |||||
| else: | |||||
| logger.warning(f'HTTP error {result} from {mirror}/{url}. Trying next mirror.') | |||||
| except TooManyRequestsException: | |||||
| logger.info(f'Received Too Many Requests error from {mirror}/{url}. Trying other mirror.') | |||||
| retry_mirrors.append(mirror) | |||||
| if retry_mirrors: | |||||
| logger.info(f"Could not find {url}, but some mirrors didn't respond. Backing off, then trying again") | |||||
| mirrors = retry_mirrors | |||||
| await asyncio.sleep(0.1) | |||||
| async with session.get(url) as response: | |||||
| if response.status == 200: | |||||
| return await response.text() | |||||
| else: | else: | ||||
| break | |||||
| logger.error(f'Download of {url} failed for all mirrors') | |||||
| return None | |||||
| logger.warning(f'HTTP error {response.status} from {url}.') | |||||
| return None | |||||
| @@ -2,7 +2,7 @@ import dataclasses | |||||
| import logging | import logging | ||||
| from typing import Optional, Iterable | from typing import Optional, Iterable | ||||
| from config import Package, MavenMirrors | |||||
| from config import Package | |||||
| from .fetch import fetch_pom | from .fetch import fetch_pom | ||||
| from .version import resolve_version | from .version import resolve_version | ||||
| from .pom import PropertyMissing, Properties | from .pom import PropertyMissing, Properties | ||||
| @@ -10,7 +10,7 @@ from .pom import PropertyMissing, Properties | |||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
| async def get_effective_packages(package: Package, mirrors: MavenMirrors) -> list[Package]: | |||||
| async def get_effective_packages(package: Package, repo: str) -> list[Package]: | |||||
| """ | """ | ||||
| Get a list of packages that is required for Gradle to fetch this package. | Get a list of packages that is required for Gradle to fetch this package. | ||||
| @@ -25,13 +25,13 @@ async def get_effective_packages(package: Package, mirrors: MavenMirrors) -> lis | |||||
| """ | """ | ||||
| versions = [ | versions = [ | ||||
| dataclasses.replace(package, version=version) | dataclasses.replace(package, version=version) | ||||
| for version in await resolve_version(package, mirrors.package_mirrors) | |||||
| for version in await resolve_version(package, repo) | |||||
| ] | ] | ||||
| deps = [ | deps = [ | ||||
| dep | dep | ||||
| for v in versions | for v in versions | ||||
| for dep in await get_dependencies(v, mirrors.package_mirrors) | |||||
| for dep in await get_dependencies(v, repo) | |||||
| ] | ] | ||||
| return versions + deps | return versions + deps | ||||
| @@ -1,14 +1,14 @@ | |||||
| import dataclasses | import dataclasses | ||||
| from config import Plugin, MavenMirrors | |||||
| from config import Plugin | |||||
| from .version import resolve_version | from .version import resolve_version | ||||
| async def get_effective_plugins(plugin: Plugin, mirrors: MavenMirrors) -> list[Plugin]: | |||||
| async def get_effective_plugins(plugin: Plugin, repo: str) -> list[Plugin]: | |||||
| if plugin.version: | if plugin.version: | ||||
| return [ | return [ | ||||
| dataclasses.replace(plugin, version=version) | dataclasses.replace(plugin, version=version) | ||||
| for version in await resolve_version(plugin.package, mirrors.plugin_mirrors) | |||||
| for version in await resolve_version(plugin.package, repo) | |||||
| ] | ] | ||||
| else: | else: | ||||
| return [plugin] | return [plugin] | ||||
| @@ -1,5 +1,4 @@ | |||||
| import logging | import logging | ||||
| from typing import Iterable | |||||
| from config import Package | from config import Package | ||||
| from .fetch import fetch_metadata | from .fetch import fetch_metadata | ||||
| @@ -7,11 +6,11 @@ from .fetch import fetch_metadata | |||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||
| async def resolve_version(package: Package, mirrors: Iterable[str]) -> list[str]: | |||||
| async def resolve_version(package: Package, repo: str) -> list[str]: | |||||
| versions = [] | versions = [] | ||||
| if package.version in ['latest', 'stable']: | if package.version in ['latest', 'stable']: | ||||
| if metadata := await fetch_metadata(package, mirrors): | |||||
| if metadata := await fetch_metadata(package, repo): | |||||
| if package.version == 'latest': | if package.version == 'latest': | ||||
| versions.append(metadata.latest) | versions.append(metadata.latest) | ||||
| elif package.version == 'stable': | elif package.version == 'stable': | ||||