diff --git a/pommer/pommer.py b/pommer/pommer.py index f9ee846..1e1e8b7 100644 --- a/pommer/pommer.py +++ b/pommer/pommer.py @@ -15,13 +15,13 @@ This YET only works with Maven! """ - 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): @@ -107,19 +107,114 @@ def parse_pom_xml(pom_path): "kotlin_version": kotlin_version, "source_dir": source_dir, "default_goal": default_goal, - "pom_path": pom_path + "pom_path": pom_path, + "build_system": "maven" } except Exception as e: print(f"Error parsing {pom_path}: {e}") return None -def generate_gitea_workflow(pom_infos): +def parse_gradle_file(gradle_path): """ - Generate a Gitea workflow YAML file based on multiple POM information + 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 POM files found") + print("No valid Maven POM files found") return None # Get the highest Java version required @@ -186,13 +281,15 @@ jobs: 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 "$GITHUB_WORKSPACE/pom.xml" -Dmaven.compiler.failOnError=true + mvn -B {maven_command} -f "{relative_pom_path}" -Dmaven.compiler.failOnError=true """ # Add artifact upload step @@ -201,8 +298,108 @@ jobs: uses: actions/upload-artifact@v3 with: name: {info["artifact_id"]} - path: target/{info['artifact_id']}-*.jar - if-no-files-found: error + 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 @@ -213,78 +410,176 @@ def find_pom_files(base_dir="."): 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/Kotlin projects') - parser.add_argument('--dir', '-d', default='.', help='Base directory to search for pom.xml files') + 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: - print(f"No pom.xml files found in {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") + print(f"Found {len(pom_files)} pom.xml files and {len(gradle_files)} Gradle files") - # Parse all POM files - pom_infos = [] + # Parse all project files + project_infos = [] + + # Parse Maven files for pom_file in pom_files: info = parse_pom_xml(pom_file) if info: - pom_infos.append(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 pom_infos: - print("No valid POM files could be parsed") - return - - # Generate the workflow content - workflow_content = generate_gitea_workflow(pom_infos) - - if not workflow_content: + 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) - # Write the workflow file - workflow_file = workflow_dir / "maven_build.yaml" - with open(workflow_file, "w") as f: - f.write(workflow_content) + # 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}") - print(f"Gitea workflow generated at: {workflow_file}") - print(f"This workflow will build {len(pom_infos)} Maven projects") + # 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 pom_infos: + for info in project_infos: kotlin_info = "with Kotlin" if info["is_kotlin"] else "Java only" - build_command = info["default_goal"] if info["default_goal"] else "clean package" - print( - f"- {info['name']} ({info['artifact_id']}): {kotlin_info}, Java {info['java_version']}, build: {build_command}") - - # Create a simple direct build script as fallback - with open("build.sh", "w") as f: - f.write("""#!/bin/bash -# Direct build script for Maven project - -echo "Current directory: $(pwd)" -echo "Building project with Maven..." - -# Run Maven build using the exact pom.xml location -mvn clean package -f "$(pwd)/pom.xml" - -echo "Build complete. JAR file should be in target/ directory." -""") - - # Make it executable - os.chmod("build.sh", 0o755) - print(f"Simple build script generated at: build.sh") + 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__":