#!/usr/bin/env python3 """ POMMER.PY THIS IS PROPRIETARY SOFTWARE DO NOT DISTRIBUTE TO OUTSIDERS! This Python File is distributed with every Kotlin Plugin Repository! If you find this to be confusing to use look at the Documentation in "rattatwinko/pommer" Run this Script with Python 3.11 ; 3.9 This works with Gradle and Maven ; other Branch is for reliablitiy ; this one is for "Support" with larger *commercial* Builds """ import os import xml.etree.ElementTree as ET import re from pathlib import Path import argparse import glob import json def parse_pom_xml(pom_path): """ Parse a pom.xml file and extract relevant information """ try: print(f"Parsing POM file: {pom_path}") # Register the default namespace ET.register_namespace('', "http://maven.apache.org/POM/4.0.0") # Parse the XML file tree = ET.parse(pom_path) root = tree.getroot() # Define namespace for easier XPath queries ns = {'mvn': "http://maven.apache.org/POM/4.0.0"} # Extract project info artifact_id = root.find('./mvn:artifactId', ns).text group_id = root.find('./mvn:groupId', ns).text if root.find('./mvn:groupId', ns) is not None else "unknown" version = root.find('./mvn:version', ns).text if root.find('./mvn:version', ns) is not None else "unknown" name = root.find('./mvn:name', ns).text if root.find('./mvn:name', ns) is not None else artifact_id # Extract Java version java_version_elem = root.find('./mvn:properties/mvn:java.version', ns) java_version = java_version_elem.text if java_version_elem is not None else "17" # Default to Java 17 if not specified # Extract packaging type (default to jar if not specified) packaging = root.find('./mvn:packaging', ns) packaging = packaging.text if packaging is not None else "jar" # Check if Kotlin is used kotlin_version_elem = root.find('./mvn:properties/mvn:kotlin.version', ns) kotlin_version = kotlin_version_elem.text if kotlin_version_elem is not None else None # Check for Kotlin plugin or dependency kotlin_plugin = None kotlin_dep = None # Check for Kotlin plugin plugins = root.findall('.//mvn:plugin', ns) for plugin in plugins: plugin_group = plugin.find('./mvn:groupId', ns) if plugin_group is not None and plugin_group.text == 'org.jetbrains.kotlin': kotlin_plugin = plugin break # Check for Kotlin dependency deps = root.findall('.//mvn:dependency', ns) for dep in deps: dep_group = dep.find('./mvn:groupId', ns) dep_artifact = dep.find('./mvn:artifactId', ns) if (dep_group is not None and dep_group.text == 'org.jetbrains.kotlin' and dep_artifact is not None and 'kotlin-stdlib' in dep_artifact.text): kotlin_dep = dep break # Determine if this is a Kotlin project is_kotlin = kotlin_version is not None or kotlin_plugin is not None or kotlin_dep is not None # Check for source directories source_dir = None source_dirs = root.findall('.//mvn:sourceDirectory', ns) if source_dirs: source_dir = source_dirs[0].text # Check for default goal (to use the same command as IntelliJ) default_goal = None default_goal_elem = root.find('./mvn:build/mvn:defaultGoal', ns) if default_goal_elem is not None: default_goal = default_goal_elem.text return { "artifact_id": artifact_id, "group_id": group_id, "version": version, "name": name, "java_version": java_version, "packaging": packaging, "is_kotlin": is_kotlin, "kotlin_version": kotlin_version, "source_dir": source_dir, "default_goal": default_goal, "pom_path": pom_path, "build_system": "maven" } except Exception as e: print(f"Error parsing {pom_path}: {e}") return None def parse_gradle_file(gradle_path): """ Parse a build.gradle or build.gradle.kts file and extract relevant information """ try: print(f"Parsing Gradle file: {gradle_path}") # Read the file content with open(gradle_path, 'r') as f: content = f.read() # Determine if it's a Kotlin DSL file is_kotlin_dsl = gradle_path.endswith('.kts') # Extract group, version and project name group_match = re.search(r'group\s*=\s*[\'"]([^\'"]+)[\'"]', content) group_id = group_match.group(1) if group_match else "unknown" version_match = re.search(r'version\s*=\s*[\'"]([^\'"]+)[\'"]', content) version = version_match.group(1) if version_match else "unknown" # Check settings.gradle for project name settings_path = os.path.join(os.path.dirname(gradle_path), "settings.gradle") settings_path_kts = os.path.join(os.path.dirname(gradle_path), "settings.gradle.kts") artifact_id = "unknown" name = "unknown" if os.path.exists(settings_path): with open(settings_path, 'r') as f: settings_content = f.read() root_project_match = re.search(r'rootProject\.name\s*=\s*[\'"]([^\'"]+)[\'"]', settings_content) if root_project_match: artifact_id = root_project_match.group(1) name = artifact_id elif os.path.exists(settings_path_kts): with open(settings_path_kts, 'r') as f: settings_content = f.read() root_project_match = re.search(r'rootProject\.name\s*=\s*[\'"]([^\'"]+)[\'"]', settings_content) if root_project_match: artifact_id = root_project_match.group(1) name = artifact_id # If no name found in settings.gradle, use directory name if artifact_id == "unknown": artifact_id = os.path.basename(os.path.dirname(gradle_path)) name = artifact_id # Check for Java version java_version = "17" # Default to Java 17 java_version_match = re.search(r'sourceCompatibility\s*=\s*[\'"]*JavaVersion\.VERSION_(\d+)[\'"]*', content) if java_version_match: java_version = java_version_match.group(1) else: # Alternative pattern: sourceCompatibility = '11' alt_java_match = re.search(r'sourceCompatibility\s*=\s*[\'"](\d+)[\'"]', content) if alt_java_match: java_version = alt_java_match.group(1) else: # Look for toolchain configuration toolchain_match = re.search(r'toolchain\s*{[^}]*languageVersion\s*=\s*JavaLanguageVersion\s*\.\s*of\s*\(\s*(\d+)\s*\)', content, re.DOTALL) if toolchain_match: java_version = toolchain_match.group(1) # Check if Kotlin is used kotlin_plugin_match = re.search(r'(id\s*\(\s*[\'"]kotlin[\'"])|(id\s*[\'"]org\.jetbrains\.kotlin)', content) kotlin_version_match = re.search(r'kotlin\s*version\s*[\'"]([^\'"]+)[\'"]', content) is_kotlin = bool(kotlin_plugin_match) or bool(kotlin_version_match) or is_kotlin_dsl kotlin_version = kotlin_version_match.group(1) if kotlin_version_match else None # Check for application plugin is_application = bool(re.search(r'id\s*\(\s*[\'"]application[\'"]', content)) return { "artifact_id": artifact_id, "group_id": group_id, "version": version, "name": name, "java_version": java_version, "packaging": "jar" if not is_application else "application", "is_kotlin": is_kotlin, "kotlin_version": kotlin_version, "source_dir": None, # Gradle uses conventional source dirs "default_goal": None, # Gradle doesn't have default goals like Maven "gradle_path": gradle_path, "is_kotlin_dsl": is_kotlin_dsl, "build_system": "gradle" } except Exception as e: print(f"Error parsing {gradle_path}: {e}") return None def generate_maven_workflow(pom_infos): """ Generate a Gitea workflow YAML file for Maven projects """ if not pom_infos: print("No valid Maven POM files found") return None # Get the highest Java version required java_version = max([info["java_version"] for info in pom_infos]) # Check if any project uses Kotlin uses_kotlin = any(info["is_kotlin"] for info in pom_infos) # Kotlin version (if any) kotlin_version = None for info in pom_infos: if info["kotlin_version"]: kotlin_version = info["kotlin_version"] break # Construct the workflow content workflow_content = f"""name: Maven Build on: push: branches: [ main, master, dev ] pull_request: branches: [ main, master ] workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up JDK {java_version} uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '{java_version}' cache: 'maven' - name: Install Maven run: | if ! command -v mvn &> /dev/null; then echo "Maven not found, installing..." sudo apt-get update sudo apt-get install -y maven fi mvn --version - name: Debug Info run: | echo "Current workspace directory: $GITHUB_WORKSPACE" echo "Current directory: $(pwd)" echo "Project structure:" find . -type f -name "*.kt" | sort find . -type f -name "pom.xml" echo "Maven version: $(mvn --version)" """ # Add individual build steps for each POM for i, info in enumerate(pom_infos): # Determine the Maven command to use (same as the default goal if specified) maven_command = "clean package" if info["default_goal"]: maven_command = info["default_goal"] relative_pom_path = info["pom_path"] workflow_content += f""" - name: Build {info["name"]} ({info["artifact_id"]}) run: | echo "Building {info["artifact_id"]}" echo "Current directory: $(pwd)" # Run Maven build directly using the POM file path mvn -B {maven_command} -f "{relative_pom_path}" -Dmaven.compiler.failOnError=true """ # Add artifact upload step workflow_content += f""" - name: Upload {info["artifact_id"]} artifact uses: actions/upload-artifact@v3 with: name: {info["artifact_id"]} path: | {os.path.dirname(relative_pom_path)}/target/{info['artifact_id']}-*.jar {os.path.dirname(relative_pom_path)}/target/*.jar if-no-files-found: warn """ return workflow_content def generate_gradle_workflow(gradle_infos): """ Generate a Gitea workflow YAML file for Gradle projects """ if not gradle_infos: print("No valid Gradle files found") return None # Get the highest Java version required java_version = max([info["java_version"] for info in gradle_infos]) # Check if any project uses Kotlin uses_kotlin = any(info["is_kotlin"] for info in gradle_infos) # Kotlin version (if any) kotlin_version = None for info in gradle_infos: if info["kotlin_version"]: kotlin_version = info["kotlin_version"] break # Construct the workflow content workflow_content = f"""name: Gradle Build on: push: branches: [ main, master, dev ] pull_request: branches: [ main, master ] workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up JDK {java_version} uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '{java_version}' cache: 'gradle' - name: Grant execute permission for gradlew run: | find . -name gradlew -type f -exec chmod +x {{}} \; - name: Debug Info run: | echo "Current workspace directory: $GITHUB_WORKSPACE" echo "Current directory: $(pwd)" echo "Project structure:" find . -type f -name "*.kt" | sort find . -type f -name "build.gradle*" if [ -f ./gradlew ]; then ./gradlew --version else echo "No wrapper found, will use system gradle" gradle --version fi """ # Add individual build steps for each Gradle project for i, info in enumerate(gradle_infos): project_dir = os.path.dirname(info["gradle_path"]) has_wrapper = os.path.exists(os.path.join(project_dir, "gradlew")) gradle_command = f"{os.path.join(project_dir, 'gradlew')}" if has_wrapper else "gradle" workflow_content += f""" - name: Build {info["name"]} ({info["artifact_id"]}) working-directory: {project_dir} run: | echo "Building {info["artifact_id"]}" echo "Current directory: $(pwd)" # Run Gradle build {"./gradlew" if has_wrapper else "gradle"} build """ # Add artifact upload step workflow_content += f""" - name: Upload {info["artifact_id"]} artifact uses: actions/upload-artifact@v3 with: name: {info["artifact_id"]} path: | {project_dir}/build/libs/*.jar {project_dir}/app/build/libs/*.jar {project_dir}/build/distributions/*.zip {project_dir}/app/build/distributions/*.zip if-no-files-found: warn """ return workflow_content def find_pom_files(base_dir="."): """Find all pom.xml files in the given directory and subdirectories""" return glob.glob(f"{base_dir}/**/pom.xml", recursive=True) def find_gradle_files(base_dir="."): """Find all build.gradle and build.gradle.kts files in the given directory and subdirectories""" gradle_files = glob.glob(f"{base_dir}/**/build.gradle", recursive=True) kotlin_gradle_files = glob.glob(f"{base_dir}/**/build.gradle.kts", recursive=True) return gradle_files + kotlin_gradle_files def generate_build_script(project_infos): """Generate a universal build script for either Maven or Gradle projects""" maven_projects = [p for p in project_infos if p["build_system"] == "maven"] gradle_projects = [p for p in project_infos if p["build_system"] == "gradle"] script = """#!/bin/bash # Universal build script for Maven and Gradle projects echo "Current directory: $(pwd)" """ if maven_projects: script += """ # Check if Maven is installed if ! command -v mvn &> /dev/null; then echo "Maven not found, installing..." sudo apt-get update sudo apt-get install -y maven fi echo "Maven version: $(mvn --version)" """ for project in maven_projects: script += f""" echo "Building Maven project: {project['name']}" mvn clean package -f "{project['pom_path']}" """ if gradle_projects: script += """ # Check for Gradle or Gradle Wrapper """ for project in gradle_projects: project_dir = os.path.dirname(project["gradle_path"]) has_wrapper = os.path.exists(os.path.join(project_dir, "gradlew")) if has_wrapper: script += f""" echo "Building Gradle project using wrapper: {project['name']}" cd "{project_dir}" chmod +x ./gradlew ./gradlew build cd - > /dev/null """ else: script += f""" echo "Building Gradle project using system Gradle: {project['name']}" # Install Gradle if not available if ! command -v gradle &> /dev/null; then echo "Gradle not found, installing..." sudo apt-get update sudo apt-get install -y gradle fi cd "{project_dir}" gradle build cd - > /dev/null """ script += """ echo "Build complete. Build artifacts should be in their respective target/ or build/libs/ directories." """ return script def main(): parser = argparse.ArgumentParser(description='Generate Gitea workflow for Maven/Gradle/Kotlin projects') parser.add_argument('--dir', '-d', default='.', help='Base directory to search for project files') parser.add_argument('--specific-pom', '-p', help='Path to a specific pom.xml file to process') parser.add_argument('--specific-gradle', '-g', help='Path to a specific build.gradle file to process') parser.add_argument('--output-json', '-j', action='store_true', help='Output project info as JSON as well') args = parser.parse_args() # Find project files pom_files = [] gradle_files = [] if args.specific_pom: pom_files = [args.specific_pom] else: pom_files = find_pom_files(args.dir) if args.specific_gradle: gradle_files = [args.specific_gradle] else: gradle_files = find_gradle_files(args.dir) if not pom_files and not gradle_files: print(f"No pom.xml or build.gradle files found in {args.dir}") return print(f"Found {len(pom_files)} pom.xml files and {len(gradle_files)} Gradle files") # Parse all project files project_infos = [] # Parse Maven files for pom_file in pom_files: info = parse_pom_xml(pom_file) if info: project_infos.append(info) # Parse Gradle files for gradle_file in gradle_files: info = parse_gradle_file(gradle_file) if info: project_infos.append(info) if not project_infos: print("No valid project files could be parsed") return # Count by build system maven_count = sum(1 for p in project_infos if p["build_system"] == "maven") gradle_count = sum(1 for p in project_infos if p["build_system"] == "gradle") # Create the .gitea/workflows directory if it doesn't exist workflow_dir = Path(".gitea/workflows") workflow_dir.mkdir(parents=True, exist_ok=True) # Generate and write workflow files for each build system if maven_count > 0: maven_infos = [p for p in project_infos if p["build_system"] == "maven"] maven_workflow = generate_maven_workflow(maven_infos) if maven_workflow: maven_workflow_file = workflow_dir / "maven_build.yaml" with open(maven_workflow_file, "w") as f: f.write(maven_workflow) print(f"Gitea Maven workflow generated at: {maven_workflow_file}") if gradle_count > 0: gradle_infos = [p for p in project_infos if p["build_system"] == "gradle"] gradle_workflow = generate_gradle_workflow(gradle_infos) if gradle_workflow: gradle_workflow_file = workflow_dir / "gradle_build.yaml" with open(gradle_workflow_file, "w") as f: f.write(gradle_workflow) print(f"Gitea Gradle workflow generated at: {gradle_workflow_file}") # Generate a universal build script build_script = generate_build_script(project_infos) with open("build.sh", "w") as f: f.write(build_script) os.chmod("build.sh", 0o755) print(f"Universal build script generated at: build.sh") # Print summary of detected projects print("\nDetected projects:") for info in project_infos: kotlin_info = "with Kotlin" if info["is_kotlin"] else "Java only" if info["build_system"] == "maven": build_command = info["default_goal"] if info["default_goal"] else "clean package" print(f"- {info['name']} ({info['artifact_id']}): Maven, {kotlin_info}, Java {info['java_version']}, build: {build_command}") else: dsl_info = "Kotlin DSL" if info.get("is_kotlin_dsl") else "Groovy DSL" print(f"- {info['name']} ({info['artifact_id']}): Gradle ({dsl_info}), {kotlin_info}, Java {info['java_version']}") # Export as JSON if requested if args.output_json: with open("project_info.json", "w") as f: json.dump(project_infos, f, indent=2) print(f"Project information exported to project_info.json") if __name__ == "__main__": main()