diff --git a/.github/workflows/jetbrains-compliance.yml b/.github/workflows/jetbrains-compliance.yml new file mode 100644 index 0000000..91077ec --- /dev/null +++ b/.github/workflows/jetbrains-compliance.yml @@ -0,0 +1,59 @@ +name: JetBrains Auto-Approval Compliance + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + compliance-check: + runs-on: ubuntu-latest + name: JetBrains Compliance Linting + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Make scripts executable + run: chmod +x ./scripts/jetbrains-compliance-check.sh + + - name: Run JetBrains Compliance Checks + run: | + echo "Running JetBrains auto-approval compliance checks..." + ./scripts/jetbrains-compliance-check.sh + + - name: Comment PR with compliance status + if: github.event_name == 'pull_request' && failure() + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: '⚠️ **JetBrains Auto-Approval Compliance Check Failed**\n\n' + + 'This PR contains code that violates JetBrains auto-approval requirements:\n\n' + + '- ❌ Do **not** use forbidden Kotlin experimental APIs\n' + + '- ❌ Do **not** add lambdas, handlers, or class handles to Java runtime hooks\n' + + '- ❌ Do **not** create threads manually (use coroutines or ensure cleanup in `CoderRemoteProvider#close()`)\n' + + '- ❌ Do **not** bundle libraries already provided by Toolbox\n' + + '- ❌ Do **not** perform ill-intentioned actions\n\n' + + 'Please check the workflow logs for detailed violations and fix them before merging.' + }) diff --git a/JETBRAINS_COMPLIANCE.md b/JETBRAINS_COMPLIANCE.md new file mode 100644 index 0000000..c625651 --- /dev/null +++ b/JETBRAINS_COMPLIANCE.md @@ -0,0 +1,97 @@ +# JetBrains Auto-Approval Compliance + +This document describes the linting setup to ensure compliance with JetBrains auto-approval requirements for Toolbox plugins. + +## Overview + +JetBrains has enabled auto-approval for this plugin, which requires following specific guidelines to maintain the approval status. This repository includes automated checks to ensure compliance. + +## Requirements + +Based on communication with JetBrains team, the following requirements must be met: + +### ✅ Allowed +- **Coroutines**: Use `coroutineScope.launch` for concurrent operations +- **Library-managed threads**: Libraries like OkHttp with their own thread pools are acceptable +- **Some experimental coroutines APIs**: `kotlinx.coroutines.selects.select` and `kotlinx.coroutines.selects.onTimeout` are acceptable +- **Proper cleanup**: Ensure resources are released in `CoderRemoteProvider#close()` method + +### ❌ Forbidden +- **Kotlin experimental APIs**: Core Kotlin experimental APIs (not coroutines-specific ones) +- **Java runtime hooks**: No lambdas, handlers, or class handles to Java runtime hooks +- **Manual thread creation**: Avoid `Thread()`, `Executors.new*()`, `ThreadPoolExecutor`, etc. +- **Bundled libraries**: Don't bundle libraries already provided by Toolbox +- **Ill-intentioned actions**: No malicious or harmful code + +## Linting Setup + +### JetBrains Compliance Check Script + +The primary compliance checking is done via a shell script: + +```bash +./scripts/jetbrains-compliance-check.sh +``` + +This script checks for: +- Forbidden experimental API usage +- Manual thread creation patterns +- Java runtime hooks +- Potentially bundled libraries +- Coroutines best practices + +### Standard Code Quality (Detekt) + +Standard Kotlin code quality is checked using Detekt: + +```bash +./gradlew detekt +``` + +## CI/CD Integration + +The GitHub Actions workflow `.github/workflows/jetbrains-compliance.yml` runs compliance checks on every PR and push. + +## Running Locally + +### Quick Compliance Check +```bash +# Run JetBrains compliance check +./scripts/jetbrains-compliance-check.sh +``` + +### Full Code Quality Check +```bash +# Run detekt for code quality +./gradlew detekt + +# View HTML report +open build/reports/detekt/detekt.html +``` + +## Understanding Results + +### Compliance Check Results + +- **✅ No critical violations**: Code complies with JetBrains requirements +- **❌ Critical violations**: Must be fixed before auto-approval +- **⚠️ Warnings**: Should be reviewed but may be acceptable + +### Common Warnings + +1. **Manual thread creation**: If you see warnings about thread creation: + - Prefer coroutines: `coroutineScope.launch { ... }` + - If using libraries with threads, ensure cleanup in `close()` + +2. **Library imports**: If you see warnings about library imports: + - Verify the library isn't bundled in the final plugin + - Check that Toolbox doesn't already provide the library + +3. **GlobalScope usage**: If you see warnings about `GlobalScope`: + - Use the coroutine scope provided by Toolbox instead + +## Resources + +- [JetBrains Toolbox Plugin Development](https://plugins.jetbrains.com/docs/toolbox/) +- [Detekt Documentation](https://detekt.dev/) +- [Kotlin Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html) diff --git a/build.gradle.kts b/build.gradle.kts index 93d13a0..8f2fc08 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,6 +22,7 @@ plugins { alias(libs.plugins.gradle.wrapper) alias(libs.plugins.changelog) alias(libs.plugins.gettext) + alias(libs.plugins.detekt) } @@ -110,6 +111,23 @@ tasks.test { useJUnitPlatform() } +// Detekt configuration for code quality +detekt { + buildUponDefaultConfig = true + allRules = false +} + +// Configure detekt for code quality reporting +tasks.withType().configureEach { + jvmTarget = "21" + reports { + html.required.set(true) + xml.required.set(true) + } + // Don't fail build on detekt issues - just report them + ignoreFailures = true +} + tasks.jar { archiveBaseName.set(extension.id) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4647eb8..e52dd3d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,6 +15,7 @@ changelog = "2.2.1" gettext = "0.7.0" plugin-structure = "3.308" mockk = "1.14.4" +detekt = "1.23.7" [libraries] toolbox-core-api = { module = "com.jetbrains.toolbox:core-api", version.ref = "toolbox-plugin-api" } @@ -45,4 +46,5 @@ dependency-license-report = { id = "com.github.jk1.dependency-license-report", v ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } gradle-wrapper = { id = "me.filippov.gradle.jvm.wrapper", version.ref = "gradle-wrapper" } changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } -gettext = { id = "name.kropp.kotlinx-gettext", version.ref = "gettext" } \ No newline at end of file +gettext = { id = "name.kropp.kotlinx-gettext", version.ref = "gettext" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } diff --git a/scripts/jetbrains-compliance-check.sh b/scripts/jetbrains-compliance-check.sh new file mode 100755 index 0000000..1dae630 --- /dev/null +++ b/scripts/jetbrains-compliance-check.sh @@ -0,0 +1,102 @@ +#!/bin/bash + +# JetBrains Auto-Approval Compliance Check Script +# This script checks for violations of JetBrains auto-approval requirements + +set -e + +echo "🔍 JetBrains Auto-Approval Compliance Check" +echo "===========================================" +echo + +VIOLATIONS=0 +SOURCE_DIR="src/main/kotlin" + +# Function to report violations +report_violation() { + echo "❌ VIOLATION: $1" + echo " File: $2" + echo " Line: $3" + echo " Context: $4" + echo + VIOLATIONS=$((VIOLATIONS + 1)) +} + +# Function to report warnings +report_warning() { + echo "⚠️ WARNING: $1" + echo " File: $2" + echo " Line: $3" + echo " Context: $4" + echo +} + +echo "1. Checking for experimental API usage..." +# Check for forbidden experimental annotations (excluding acceptable coroutines ones) +grep -rn "@ExperimentalApi\|@ExperimentalStdlibApi\|@ExperimentalUnsignedTypes\|@ExperimentalContracts\|@ExperimentalTypeInference\|@InternalCoroutinesApi\|@ExperimentalTime" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_violation "Forbidden experimental API usage" "$file" "$line" "$content" +done + +# Check for @OptIn with forbidden experimental APIs +grep -rn "@OptIn.*ExperimentalApi\|@OptIn.*ExperimentalStdlibApi\|@OptIn.*InternalCoroutinesApi" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_violation "@OptIn with forbidden experimental API" "$file" "$line" "$content" +done + +echo "2. Checking for manual thread creation..." +# Check for direct thread creation +grep -rn "Thread(\|ThreadPoolExecutor\|ScheduledThreadPoolExecutor\|ForkJoinPool\|Timer(\|TimerTask" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_warning "Manual thread creation detected - ensure proper cleanup in CoderRemoteProvider#close()" "$file" "$line" "$content" +done + +# Check for Executors usage +grep -rn "Executors\.new\|CompletableFuture\.runAsync\|CompletableFuture\.supplyAsync" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_warning "Executor/CompletableFuture usage detected - ensure proper cleanup in CoderRemoteProvider#close()" "$file" "$line" "$content" +done + +# Check for classes extending Thread or implementing Runnable +grep -rn "class.*extends Thread\|class.*implements Runnable\|: Thread\|: Runnable" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_warning "Class extending Thread or implementing Runnable - consider using coroutines" "$file" "$line" "$content" +done + +echo "3. Checking for Java runtime hooks..." +# Check for runtime hooks +grep -rn "Runtime\..*addShutdownHook\|System\.setSecurityManager\|setUncaughtExceptionHandler\|setDefaultUncaughtExceptionHandler" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_violation "Java runtime hook usage forbidden" "$file" "$line" "$content" +done + +# Check for suspicious system property modifications +grep -rn "System\.setProperty.*java\.security\|System\.setProperty.*java\.awt\.headless\|System\.setProperty.*file\.encoding" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_violation "Suspicious system property modification" "$file" "$line" "$content" +done + +echo "4. Checking for bundled libraries..." +# Check for imports that might indicate bundled libraries +grep -rn "import org\.slf4j\|import org\.jetbrains\.annotations" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_warning "Import of potentially bundled library - ensure it's not bundled" "$file" "$line" "$content" +done + +echo "5. Checking for coroutines best practices..." +# Check for GlobalScope usage (should use provided scope) +grep -rn "GlobalScope\.launch\|GlobalScope\.async" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do + report_warning "GlobalScope usage detected - consider using provided coroutine scope" "$file" "$line" "$content" +done + +echo "===========================================" +if [ $VIOLATIONS -eq 0 ]; then + echo "✅ No critical violations found!" + echo " Your code appears to comply with JetBrains auto-approval requirements." + echo + echo "📋 Summary of requirements:" + echo " ✓ No forbidden Kotlin experimental APIs" + echo " ✓ No Java runtime hooks" + echo " ✓ No suspicious system modifications" + echo " ⚠️ Manual thread creation warnings (if any) - ensure cleanup in close()" + echo " ⚠️ Library bundling warnings (if any) - verify not bundling Toolbox libs" + echo + exit 0 +else + echo "❌ Found $VIOLATIONS critical violations!" + echo " Please fix these issues before submitting for auto-approval." + echo + exit 1 +fi