Skip to content

Commit c4c3d6d

Browse files
feat: Add JetBrains auto-approval compliance linter
Implements comprehensive linting to ensure compliance with JetBrains auto-approval requirements for Toolbox plugins. Features: - Shell script for JetBrains compliance checking - Detekt integration for code quality - GitHub Actions workflow for CI/CD - Comprehensive documentation Compliance checks: - Forbidden experimental API usage detection - Manual thread creation warnings - Java runtime hooks detection - Library bundling warnings - Coroutines best practices Based on clarified requirements from JetBrains team allowing: - coroutineScope.launch usage - Library-managed threads (with proper cleanup) - Some coroutines experimental APIs (select, onTimeout) Co-authored-by: matifali <[email protected]>
1 parent 5d05fff commit c4c3d6d

File tree

5 files changed

+279
-1
lines changed

5 files changed

+279
-1
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: JetBrains Auto-Approval Compliance
2+
3+
on:
4+
push:
5+
branches: [ main, develop ]
6+
pull_request:
7+
branches: [ main, develop ]
8+
9+
jobs:
10+
compliance-check:
11+
runs-on: ubuntu-latest
12+
name: JetBrains Compliance Linting
13+
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
18+
- name: Set up JDK 21
19+
uses: actions/setup-java@v4
20+
with:
21+
java-version: '21'
22+
distribution: 'temurin'
23+
24+
- name: Cache Gradle packages
25+
uses: actions/cache@v4
26+
with:
27+
path: |
28+
~/.gradle/caches
29+
~/.gradle/wrapper
30+
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
31+
restore-keys: |
32+
${{ runner.os }}-gradle-
33+
34+
- name: Make scripts executable
35+
run: chmod +x ./scripts/jetbrains-compliance-check.sh
36+
37+
- name: Run JetBrains Compliance Checks
38+
run: |
39+
echo "Running JetBrains auto-approval compliance checks..."
40+
./scripts/jetbrains-compliance-check.sh
41+
42+
- name: Comment PR with compliance status
43+
if: github.event_name == 'pull_request' && failure()
44+
uses: actions/github-script@v7
45+
with:
46+
script: |
47+
github.rest.issues.createComment({
48+
issue_number: context.issue.number,
49+
owner: context.repo.owner,
50+
repo: context.repo.repo,
51+
body: '⚠️ **JetBrains Auto-Approval Compliance Check Failed**\n\n' +
52+
'This PR contains code that violates JetBrains auto-approval requirements:\n\n' +
53+
'- ❌ Do **not** use forbidden Kotlin experimental APIs\n' +
54+
'- ❌ Do **not** add lambdas, handlers, or class handles to Java runtime hooks\n' +
55+
'- ❌ Do **not** create threads manually (use coroutines or ensure cleanup in `CoderRemoteProvider#close()`)\n' +
56+
'- ❌ Do **not** bundle libraries already provided by Toolbox\n' +
57+
'- ❌ Do **not** perform ill-intentioned actions\n\n' +
58+
'Please check the workflow logs for detailed violations and fix them before merging.'
59+
})

JETBRAINS_COMPLIANCE.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# JetBrains Auto-Approval Compliance
2+
3+
This document describes the linting setup to ensure compliance with JetBrains auto-approval requirements for Toolbox plugins.
4+
5+
## Overview
6+
7+
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.
8+
9+
## Requirements
10+
11+
Based on communication with JetBrains team, the following requirements must be met:
12+
13+
### ✅ Allowed
14+
- **Coroutines**: Use `coroutineScope.launch` for concurrent operations
15+
- **Library-managed threads**: Libraries like OkHttp with their own thread pools are acceptable
16+
- **Some experimental coroutines APIs**: `kotlinx.coroutines.selects.select` and `kotlinx.coroutines.selects.onTimeout` are acceptable
17+
- **Proper cleanup**: Ensure resources are released in `CoderRemoteProvider#close()` method
18+
19+
### ❌ Forbidden
20+
- **Kotlin experimental APIs**: Core Kotlin experimental APIs (not coroutines-specific ones)
21+
- **Java runtime hooks**: No lambdas, handlers, or class handles to Java runtime hooks
22+
- **Manual thread creation**: Avoid `Thread()`, `Executors.new*()`, `ThreadPoolExecutor`, etc.
23+
- **Bundled libraries**: Don't bundle libraries already provided by Toolbox
24+
- **Ill-intentioned actions**: No malicious or harmful code
25+
26+
## Linting Setup
27+
28+
### JetBrains Compliance Check Script
29+
30+
The primary compliance checking is done via a shell script:
31+
32+
```bash
33+
./scripts/jetbrains-compliance-check.sh
34+
```
35+
36+
This script checks for:
37+
- Forbidden experimental API usage
38+
- Manual thread creation patterns
39+
- Java runtime hooks
40+
- Potentially bundled libraries
41+
- Coroutines best practices
42+
43+
### Standard Code Quality (Detekt)
44+
45+
Standard Kotlin code quality is checked using Detekt:
46+
47+
```bash
48+
./gradlew detekt
49+
```
50+
51+
## CI/CD Integration
52+
53+
The GitHub Actions workflow `.github/workflows/jetbrains-compliance.yml` runs compliance checks on every PR and push.
54+
55+
## Running Locally
56+
57+
### Quick Compliance Check
58+
```bash
59+
# Run JetBrains compliance check
60+
./scripts/jetbrains-compliance-check.sh
61+
```
62+
63+
### Full Code Quality Check
64+
```bash
65+
# Run detekt for code quality
66+
./gradlew detekt
67+
68+
# View HTML report
69+
open build/reports/detekt/detekt.html
70+
```
71+
72+
## Understanding Results
73+
74+
### Compliance Check Results
75+
76+
- **✅ No critical violations**: Code complies with JetBrains requirements
77+
- **❌ Critical violations**: Must be fixed before auto-approval
78+
- **⚠️ Warnings**: Should be reviewed but may be acceptable
79+
80+
### Common Warnings
81+
82+
1. **Manual thread creation**: If you see warnings about thread creation:
83+
- Prefer coroutines: `coroutineScope.launch { ... }`
84+
- If using libraries with threads, ensure cleanup in `close()`
85+
86+
2. **Library imports**: If you see warnings about library imports:
87+
- Verify the library isn't bundled in the final plugin
88+
- Check that Toolbox doesn't already provide the library
89+
90+
3. **GlobalScope usage**: If you see warnings about `GlobalScope`:
91+
- Use the coroutine scope provided by Toolbox instead
92+
93+
## Resources
94+
95+
- [JetBrains Toolbox Plugin Development](https://plugins.jetbrains.com/docs/toolbox/)
96+
- [Detekt Documentation](https://detekt.dev/)
97+
- [Kotlin Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html)

build.gradle.kts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ plugins {
2222
alias(libs.plugins.gradle.wrapper)
2323
alias(libs.plugins.changelog)
2424
alias(libs.plugins.gettext)
25+
alias(libs.plugins.detekt)
2526
}
2627

2728

@@ -110,6 +111,23 @@ tasks.test {
110111
useJUnitPlatform()
111112
}
112113

114+
// Detekt configuration for code quality
115+
detekt {
116+
buildUponDefaultConfig = true
117+
allRules = false
118+
}
119+
120+
// Configure detekt for code quality reporting
121+
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
122+
jvmTarget = "21"
123+
reports {
124+
html.required.set(true)
125+
xml.required.set(true)
126+
}
127+
// Don't fail build on detekt issues - just report them
128+
ignoreFailures = true
129+
}
130+
113131

114132
tasks.jar {
115133
archiveBaseName.set(extension.id)

gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ changelog = "2.2.1"
1515
gettext = "0.7.0"
1616
plugin-structure = "3.308"
1717
mockk = "1.14.4"
18+
detekt = "1.23.7"
1819

1920
[libraries]
2021
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
4546
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
4647
gradle-wrapper = { id = "me.filippov.gradle.jvm.wrapper", version.ref = "gradle-wrapper" }
4748
changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
48-
gettext = { id = "name.kropp.kotlinx-gettext", version.ref = "gettext" }
49+
gettext = { id = "name.kropp.kotlinx-gettext", version.ref = "gettext" }
50+
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }

scripts/jetbrains-compliance-check.sh

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/bin/bash
2+
3+
# JetBrains Auto-Approval Compliance Check Script
4+
# This script checks for violations of JetBrains auto-approval requirements
5+
6+
set -e
7+
8+
echo "🔍 JetBrains Auto-Approval Compliance Check"
9+
echo "==========================================="
10+
echo
11+
12+
VIOLATIONS=0
13+
SOURCE_DIR="src/main/kotlin"
14+
15+
# Function to report violations
16+
report_violation() {
17+
echo "❌ VIOLATION: $1"
18+
echo " File: $2"
19+
echo " Line: $3"
20+
echo " Context: $4"
21+
echo
22+
VIOLATIONS=$((VIOLATIONS + 1))
23+
}
24+
25+
# Function to report warnings
26+
report_warning() {
27+
echo "⚠️ WARNING: $1"
28+
echo " File: $2"
29+
echo " Line: $3"
30+
echo " Context: $4"
31+
echo
32+
}
33+
34+
echo "1. Checking for experimental API usage..."
35+
# Check for forbidden experimental annotations (excluding acceptable coroutines ones)
36+
grep -rn "@ExperimentalApi\|@ExperimentalStdlibApi\|@ExperimentalUnsignedTypes\|@ExperimentalContracts\|@ExperimentalTypeInference\|@InternalCoroutinesApi\|@ExperimentalTime" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
37+
report_violation "Forbidden experimental API usage" "$file" "$line" "$content"
38+
done
39+
40+
# Check for @OptIn with forbidden experimental APIs
41+
grep -rn "@OptIn.*ExperimentalApi\|@OptIn.*ExperimentalStdlibApi\|@OptIn.*InternalCoroutinesApi" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
42+
report_violation "@OptIn with forbidden experimental API" "$file" "$line" "$content"
43+
done
44+
45+
echo "2. Checking for manual thread creation..."
46+
# Check for direct thread creation
47+
grep -rn "Thread(\|ThreadPoolExecutor\|ScheduledThreadPoolExecutor\|ForkJoinPool\|Timer(\|TimerTask" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
48+
report_warning "Manual thread creation detected - ensure proper cleanup in CoderRemoteProvider#close()" "$file" "$line" "$content"
49+
done
50+
51+
# Check for Executors usage
52+
grep -rn "Executors\.new\|CompletableFuture\.runAsync\|CompletableFuture\.supplyAsync" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
53+
report_warning "Executor/CompletableFuture usage detected - ensure proper cleanup in CoderRemoteProvider#close()" "$file" "$line" "$content"
54+
done
55+
56+
# Check for classes extending Thread or implementing Runnable
57+
grep -rn "class.*extends Thread\|class.*implements Runnable\|: Thread\|: Runnable" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
58+
report_warning "Class extending Thread or implementing Runnable - consider using coroutines" "$file" "$line" "$content"
59+
done
60+
61+
echo "3. Checking for Java runtime hooks..."
62+
# Check for runtime hooks
63+
grep -rn "Runtime\..*addShutdownHook\|System\.setSecurityManager\|setUncaughtExceptionHandler\|setDefaultUncaughtExceptionHandler" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
64+
report_violation "Java runtime hook usage forbidden" "$file" "$line" "$content"
65+
done
66+
67+
# Check for suspicious system property modifications
68+
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
69+
report_violation "Suspicious system property modification" "$file" "$line" "$content"
70+
done
71+
72+
echo "4. Checking for bundled libraries..."
73+
# Check for imports that might indicate bundled libraries
74+
grep -rn "import org\.slf4j\|import org\.jetbrains\.annotations" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
75+
report_warning "Import of potentially bundled library - ensure it's not bundled" "$file" "$line" "$content"
76+
done
77+
78+
echo "5. Checking for coroutines best practices..."
79+
# Check for GlobalScope usage (should use provided scope)
80+
grep -rn "GlobalScope\.launch\|GlobalScope\.async" $SOURCE_DIR 2>/dev/null | while IFS=: read -r file line content; do
81+
report_warning "GlobalScope usage detected - consider using provided coroutine scope" "$file" "$line" "$content"
82+
done
83+
84+
echo "==========================================="
85+
if [ $VIOLATIONS -eq 0 ]; then
86+
echo "✅ No critical violations found!"
87+
echo " Your code appears to comply with JetBrains auto-approval requirements."
88+
echo
89+
echo "📋 Summary of requirements:"
90+
echo " ✓ No forbidden Kotlin experimental APIs"
91+
echo " ✓ No Java runtime hooks"
92+
echo " ✓ No suspicious system modifications"
93+
echo " ⚠️ Manual thread creation warnings (if any) - ensure cleanup in close()"
94+
echo " ⚠️ Library bundling warnings (if any) - verify not bundling Toolbox libs"
95+
echo
96+
exit 0
97+
else
98+
echo "❌ Found $VIOLATIONS critical violations!"
99+
echo " Please fix these issues before submitting for auto-approval."
100+
echo
101+
exit 1
102+
fi

0 commit comments

Comments
 (0)