From 89844c34da642635a38a75671b2379d075df4f8a Mon Sep 17 00:00:00 2001 From: Shamith Nakka Date: Fri, 31 Oct 2025 10:39:10 +0530 Subject: [PATCH] Update java-linter.yml This is a temporary standalone implementation of java-linter. These changes have been made since the marketplace workflow is currently down. Note that this workflow fails if the source code has syntax errors. --- .github/workflows/java-linter.yml | 496 +++++++++++++++++++++++++++--- 1 file changed, 447 insertions(+), 49 deletions(-) diff --git a/.github/workflows/java-linter.yml b/.github/workflows/java-linter.yml index 84ab5a0..d48ea0c 100644 --- a/.github/workflows/java-linter.yml +++ b/.github/workflows/java-linter.yml @@ -2,60 +2,458 @@ name: java-linter on: pull_request: - branches: [main] - paths: ['**.java'] - workflow_call: - + branches: [ main ] + paths: + - '**.java' + +permissions: + contents: read + pull-requests: write + checks: write + jobs: - java-linter: + java-lint-and-review: runs-on: ubuntu-latest + steps: - - name: Check out code - uses: actions/checkout@v4.1.0 - - name: Run check style - uses: nikitasavinov/checkstyle-action@0.6.0 + - name: Checkout code + uses: actions/checkout@v4 with: - level: warning - fail_on_error: true + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - need-help: - runs-on: ubuntu-latest - needs: [java-linter] - if: ${{ failure() }} - steps: - - name: Check out code - uses: actions/checkout@v4.1.0 - - name: Setting up python - uses: actions/setup-python@v4.7.1 + - name: Fetch PR base branch + if: github.event_name == 'pull_request' + run: | + git fetch origin ${{ github.event.pull_request.base.ref }}:refs/remotes/origin/${{ github.event.pull_request.base.ref }} + + - name: Detect build tool + id: detect-build + run: | + if [ -f "pom.xml" ]; then + echo "build_tool=maven" >> $GITHUB_OUTPUT + echo "Build tool: Maven" + elif [ -f "build.gradle" ] || [ -f "build.gradle.kts" ]; then + echo "build_tool=gradle" >> $GITHUB_OUTPUT + echo "Build tool: Gradle" + else + echo "build_tool=none" >> $GITHUB_OUTPUT + echo "No build tool detected" + fi + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: ${{ steps.detect-build.outputs.build_tool != 'none' && steps.detect-build.outputs.build_tool || '' }} + + - name: Cache dependencies + uses: actions/cache@v3 + if: steps.detect-build.outputs.build_tool != 'none' + with: + path: | + ~/.m2/repository + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-${{ steps.detect-build.outputs.build_tool }}-${{ hashFiles('**/pom.xml', '**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-${{ steps.detect-build.outputs.build_tool }}- + + - name: Install Checkstyle + run: | + mkdir -p $HOME/tools + cd $HOME/tools + wget https://github.com/checkstyle/checkstyle/releases/download/checkstyle-10.12.5/checkstyle-10.12.5-all.jar + echo "CHECKSTYLE_JAR=$HOME/tools/checkstyle-10.12.5-all.jar" >> $GITHUB_ENV + + - name: Download Google Java Format + run: | + cd $HOME/tools + wget https://github.com/google/google-java-format/releases/download/v1.19.1/google-java-format-1.19.1-all-deps.jar + echo "FORMATTER_JAR=$HOME/tools/google-java-format-1.19.1-all-deps.jar" >> $GITHUB_ENV + + - name: Create Checkstyle Configuration + run: | + cat > checkstyle.xml << 'EOF' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EOF + + - name: Get changed Java files + id: changed-files + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + HEAD_SHA="${{ github.sha }}" + + echo "Comparing $BASE_SHA...$HEAD_SHA" + + MERGE_BASE=$(git merge-base $BASE_SHA $HEAD_SHA) + echo "Merge base: $MERGE_BASE" + + git diff --name-only --diff-filter=ACMRT $MERGE_BASE $HEAD_SHA | grep '\.java$' > changed_files.txt || true + else + if git rev-parse HEAD^ >/dev/null 2>&1; then + git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | grep '\.java$' > changed_files.txt || true + else + git diff-tree --no-commit-id --name-only --diff-filter=ACMRT -r HEAD | grep '\.java$' > changed_files.txt || true + fi + fi + + if [ -s changed_files.txt ]; then + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "Changed Java files:" + cat changed_files.txt + else + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "No Java files changed" + fi + + - name: Run Checkstyle + if: steps.changed-files.outputs.has_changes == 'true' + run: | + mkdir -p reports + java -jar $CHECKSTYLE_JAR -c checkstyle.xml $(cat changed_files.txt | tr '\n' ' ') -f xml -o reports/checkstyle-result.xml || true + java -jar $CHECKSTYLE_JAR -c checkstyle.xml $(cat changed_files.txt | tr '\n' ' ') || true + + - name: Run SpotBugs + if: steps.changed-files.outputs.has_changes == 'true' && steps.detect-build.outputs.build_tool == 'maven' + continue-on-error: true + run: | + mvn clean compile spotbugs:spotbugs || true + + - name: Run SpotBugs (Gradle) + if: steps.changed-files.outputs.has_changes == 'true' && steps.detect-build.outputs.build_tool == 'gradle' + continue-on-error: true + run: | + if grep -q "spotbugs" build.gradle* 2>/dev/null; then + ./gradlew spotbugsMain || true + else + echo "SpotBugs not configured in Gradle project" + fi + + - name: Format Java files + if: steps.changed-files.outputs.has_changes == 'true' + run: | + mkdir -p formatted + while IFS= read -r file; do + if [ -f "$file" ]; then + echo "Formatting $file..." + java -jar $FORMATTER_JAR --replace "$file" + fi + done < changed_files.txt + + - name: Generate diff and suggestions + if: steps.changed-files.outputs.has_changes == 'true' + id: generate-diff + run: | + mkdir -p suggestions + + cat > suggestions/review.md << 'HEADER' + ## šŸ” Java Code Review Results + + ### Summary + This automated review checks your Java code for: + - **Style violations** (Checkstyle) + - **Code formatting** (Google Java Format) + - **Best practices** + + --- + + HEADER + + if git diff --quiet; then + echo "no_changes=true" >> $GITHUB_OUTPUT + echo "āœ… **All files are properly formatted!**" >> suggestions/review.md + else + echo "no_changes=false" >> $GITHUB_OUTPUT + echo "### šŸ“ Formatting Changes Required" >> suggestions/review.md + echo "" >> suggestions/review.md + + while IFS= read -r file; do + if [ -f "$file" ] && ! git diff --quiet "$file"; then + echo "#### \`$file\`" >> suggestions/review.md + echo "" >> suggestions/review.md + echo "
" >> suggestions/review.md + echo "View suggested changes" >> suggestions/review.md + echo "" >> suggestions/review.md + echo '```diff' >> suggestions/review.md + git diff "$file" >> suggestions/review.md + echo '```' >> suggestions/review.md + echo "" >> suggestions/review.md + echo "
" >> suggestions/review.md + echo "" >> suggestions/review.md + fi + done < changed_files.txt + fi + + if [ -f "reports/checkstyle-result.xml" ]; then + ERROR_COUNT=$(grep -o '> suggestions/review.md + echo "### āš ļø Checkstyle Issues Found: $ERROR_COUNT" >> suggestions/review.md + echo "" >> suggestions/review.md + echo "
" >> suggestions/review.md + echo "View Checkstyle violations" >> suggestions/review.md + echo "" >> suggestions/review.md + java -jar $CHECKSTYLE_JAR -c checkstyle.xml $(cat changed_files.txt | tr '\n' ' ') 2>&1 | head -100 >> suggestions/review.md || true + echo "" >> suggestions/review.md + echo "
" >> suggestions/review.md + fi + fi + + cat >> suggestions/review.md << 'FOOTER' + + --- + + ### šŸ’” Recommendations + + 1. **Apply formatting**: Run `google-java-format` on your files + 2. **Fix Checkstyle issues**: Address the violations mentioned above + 3. **Review best practices**: Ensure your code follows Java conventions + + ### šŸ› ļø How to Fix Locally + + ```bash + # Download Google Java Format + wget https://github.com/google/google-java-format/releases/download/v1.19.1/google-java-format-1.19.1-all-deps.jar + + # Format your Java files + java -jar google-java-format-1.19.1-all-deps.jar --replace src/**/*.java + ``` + + Or use your IDE plugin: + - **IntelliJ IDEA**: Install "google-java-format" plugin + - **Eclipse**: Install "google-java-format" plugin + - **VS Code**: Install "Language Support for Java" extension + + FOOTER + + - name: Comment PR with review + if: github.event_name == 'pull_request' && steps.changed-files.outputs.has_changes == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const reviewContent = fs.readFileSync('suggestions/review.md', 'utf8'); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(comment => + comment.user.type === 'Bot' && + comment.body.includes('šŸ” Java Code Review Results') + ); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body: reviewContent + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: reviewContent + }); + } + + - name: Upload formatted files as artifact + if: steps.changed-files.outputs.has_changes == 'true' && steps.generate-diff.outputs.no_changes == 'false' + uses: actions/upload-artifact@v4 with: - python-version: '3.12' - - name: Install Clang - uses: egor-tensin/setup-clang@v1 - - name: Getting PR details - run: | - touch pr.json # creating empty file for paths - gh pr view $PR_NUMBER --json files > pr.json # storing file paths - touch res # creating empty file for final output - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Python script for clang diff - uses: jannekem/run-python-script-action@v1 + name: formatted-java-files + path: | + **/*.java + retention-days: 7 + + - name: Upload Checkstyle report + if: steps.changed-files.outputs.has_changes == 'true' && hashFiles('reports/checkstyle-result.xml') != '' + uses: actions/upload-artifact@v4 + with: + name: checkstyle-report + path: reports/checkstyle-result.xml + retention-days: 14 + + - name: Create check run + if: steps.changed-files.outputs.has_changes == 'true' + uses: actions/github-script@v7 with: + github-token: ${{ secrets.GITHUB_TOKEN }} script: | - import os - import json - import sys - with open('pr.json','r') as json_file: - data = json.load(json_file) - for file in data["files"]: - path = file["path"] - if os.path.exists(path): - os.system('echo "" >> res') - os.system(f'echo "Filename: {path}" >> res') - os.system('echo "" >> res') - os.system(f'clang-format -style=Google {path} >> res') - os.system('echo ---------------------------------------- >> res') - - name: Show diffs and exit - run: | - cat res + const fs = require('fs'); + let conclusion = 'success'; + let summary = 'āœ… All checks passed!'; + + if (fs.existsSync('reports/checkstyle-result.xml')) { + const content = fs.readFileSync('reports/checkstyle-result.xml', 'utf8'); + const errorCount = (content.match(/ 0) { + conclusion = 'failure'; + summary = `āŒ Found ${errorCount} Checkstyle violations`; + } + } + + if ('${{ steps.generate-diff.outputs.no_changes }}' === 'false') { + conclusion = 'failure'; + summary += '\nāŒ Code formatting issues detected'; + } + + await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: 'Java Lint & Format Check', + head_sha: context.sha, + status: 'completed', + conclusion: conclusion, + output: { + title: 'Java Code Quality Check', + summary: summary + } + }); + + - name: Fail if issues found + if: steps.changed-files.outputs.has_changes == 'true' && steps.generate-diff.outputs.no_changes == 'false' + run: | + echo "āŒ Code formatting or style issues detected!" + echo "Please review the PR comment for detailed suggestions." + exit 1