- Notifications
You must be signed in to change notification settings - Fork1
chore: add JetBrains auto-approval compliance linter#139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
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 gradlew executable | ||
run: chmod +x ./gradlew | ||
- name: Run JetBrains Compliance Checks | ||
run: | | ||
echo "Running JetBrains auto-approval compliance checks with detekt..." | ||
./gradlew detekt | ||
- name: Upload detekt reports | ||
uses: actions/upload-artifact@v4 | ||
if: always() | ||
with: | ||
name: detekt-reports | ||
path: | | ||
build/reports/detekt/ | ||
retention-days: 30 | ||
- 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.' | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# 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 with Detekt | ||
The primary compliance checking is done using Detekt with custom configuration in `detekt.yml`: | ||
```bash | ||
./gradlew detekt | ||
``` | ||
This configuration includes JetBrains-specific rules that check for: | ||
- **ForbiddenAnnotation**: Detects forbidden experimental API usage | ||
- **ForbiddenMethodCall**: Detects Java runtime hooks and manual thread creation | ||
- **ForbiddenImport**: Detects potentially bundled libraries | ||
- **Standard code quality rules**: Complexity, naming, performance, etc. | ||
## CI/CD Integration | ||
The GitHub Actions workflow `.github/workflows/jetbrains-compliance.yml` runs compliance checks on every PR and push. | ||
## Running Locally | ||
```bash | ||
# Run JetBrains compliance and code quality check | ||
./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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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,24 @@ tasks.test { | ||
useJUnitPlatform() | ||
} | ||
// Detekt configuration for JetBrains compliance and code quality | ||
detekt { | ||
config.setFrom("$projectDir/detekt.yml") | ||
buildUponDefaultConfig = true | ||
allRules = false | ||
} | ||
// Configure detekt for JetBrains compliance and code quality | ||
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Minor: we can do a static import here. | ||
jvmTarget = "21" | ||
reports { | ||
html.required.set(true) | ||
xml.required.set(true) | ||
} | ||
// Fail build on detekt issues for JetBrains compliance | ||
ignoreFailures = false | ||
} | ||
tasks.jar { | ||
archiveBaseName.set(extension.id) | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
# Detekt configuration for JetBrains Toolbox Plugin Auto-Approval Compliance | ||
# Based on clarified requirements from JetBrains team | ||
build: | ||
maxIssues: 1000 # Allow many issues for code quality reporting | ||
excludeCorrectable: false | ||
config: | ||
validation: true | ||
warningsAsErrors: false # Don't treat warnings as errors | ||
checkExhaustiveness: false | ||
# CRITICAL: JetBrains Compliance Rules using detekt built-in rules | ||
style: | ||
active: true | ||
# JetBrains Auto-Approval Compliance: Forbidden experimental annotations | ||
ForbiddenAnnotation: | ||
active: true | ||
annotations: | ||
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed' | ||
value: 'kotlin.ExperimentalStdlibApi' | ||
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed' | ||
value: 'kotlin.ExperimentalUnsignedTypes' | ||
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed' | ||
value: 'kotlin.contracts.ExperimentalContracts' | ||
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed' | ||
value: 'kotlin.experimental.ExperimentalTypeInference' | ||
- reason: 'Forbidden for JetBrains auto-approval: Internal coroutines APIs should be avoided' | ||
value: 'kotlinx.coroutines.InternalCoroutinesApi' | ||
- reason: 'Forbidden for JetBrains auto-approval: Experimental time APIs are not allowed' | ||
value: 'kotlin.time.ExperimentalTime' | ||
# Note: ExperimentalCoroutinesApi, DelicateCoroutinesApi, FlowPreview are acceptable | ||
# based on JetBrains feedback about select/onTimeout being OK | ||
# JetBrains Auto-Approval Compliance: Forbidden method calls | ||
ForbiddenMethodCall: | ||
active: true | ||
methods: | ||
# Java runtime hooks - forbidden | ||
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed' | ||
value: 'java.lang.Runtime.addShutdownHook' | ||
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed' | ||
value: 'java.lang.System.setSecurityManager' | ||
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed' | ||
value: 'java.lang.Thread.setUncaughtExceptionHandler' | ||
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed' | ||
value: 'java.lang.Thread.setDefaultUncaughtExceptionHandler' | ||
# Manual thread creation - warnings (allowed with proper cleanup) | ||
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()' | ||
value: 'java.lang.Thread.<init>' | ||
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()' | ||
value: 'java.util.concurrent.Executors.newFixedThreadPool' | ||
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()' | ||
value: 'java.util.concurrent.Executors.newCachedThreadPool' | ||
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()' | ||
value: 'java.util.concurrent.Executors.newSingleThreadExecutor' | ||
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()' | ||
value: 'java.util.concurrent.CompletableFuture.runAsync' | ||
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()' | ||
value: 'java.util.concurrent.CompletableFuture.supplyAsync' | ||
# JetBrains Auto-Approval Compliance: Forbidden imports | ||
ForbiddenImport: | ||
active: true | ||
imports: | ||
# Potentially bundled libraries - warnings | ||
- reason: 'Warning for JetBrains auto-approval: Ensure slf4j is not bundled - it is provided by Toolbox' | ||
value: 'org.slf4j.*' | ||
- reason: 'Warning for JetBrains auto-approval: Ensure annotations library is not bundled - it is provided by Toolbox' | ||
value: 'org.jetbrains.annotations.*' | ||
# Runtime hook classes - forbidden | ||
- reason: 'Forbidden for JetBrains auto-approval: Runtime hook classes are not allowed' | ||
value: 'java.lang.Runtime' | ||
- reason: 'Forbidden for JetBrains auto-approval: Security manager modifications are not allowed' | ||
value: 'java.security.SecurityManager' | ||
# Other important style rules | ||
MagicNumber: | ||
active: true | ||
ignoreNumbers: | ||
- '-1' | ||
- '0' | ||
- '1' | ||
- '2' | ||
ignoreHashCodeFunction: true | ||
ignorePropertyDeclaration: false | ||
ignoreLocalVariableDeclaration: false | ||
ignoreConstantDeclaration: true | ||
ignoreCompanionObjectPropertyDeclaration: true | ||
ignoreAnnotation: false | ||
ignoreNamedArgument: true | ||
ignoreEnums: false | ||
ignoreRanges: false | ||
ignoreExtensionFunctions: true | ||
MaxLineLength: | ||
active: true | ||
maxLineLength: 120 | ||
excludePackageStatements: true | ||
excludeImportStatements: true | ||
excludeCommentStatements: false | ||
NewLineAtEndOfFile: | ||
active: true | ||
WildcardImport: | ||
active: true | ||
# Essential built-in rules for basic code quality | ||
complexity: | ||
active: true | ||
CyclomaticComplexMethod: | ||
active: true | ||
threshold: 15 | ||
LongMethod: | ||
active: true | ||
threshold: 60 | ||
LongParameterList: | ||
active: true | ||
functionThreshold: 6 | ||
constructorThreshold: 7 | ||
NestedBlockDepth: | ||
active: true | ||
threshold: 4 | ||
coroutines: | ||
active: true | ||
GlobalCoroutineUsage: | ||
active: true | ||
RedundantSuspendModifier: | ||
active: true | ||
SleepInsteadOfDelay: | ||
active: true | ||
exceptions: | ||
active: true | ||
ExceptionRaisedInUnexpectedLocation: | ||
active: true | ||
ObjectExtendsThrowable: | ||
active: true | ||
PrintStackTrace: | ||
active: true | ||
ReturnFromFinally: | ||
active: true | ||
SwallowedException: | ||
active: true | ||
ThrowingExceptionFromFinally: | ||
active: true | ||
ThrowingExceptionsWithoutMessageOrCause: | ||
active: true | ||
TooGenericExceptionCaught: | ||
active: true | ||
TooGenericExceptionThrown: | ||
active: true | ||
naming: | ||
active: true | ||
ClassNaming: | ||
active: true | ||
classPattern: '[A-Z][a-zA-Z0-9]*' | ||
FunctionNaming: | ||
active: true | ||
functionPattern: '[a-z][a-zA-Z0-9]*' | ||
PackageNaming: | ||
active: true | ||
packagePattern: '[a-z]+(\.?[a-z][A-Za-z0-9]*)*' | ||
VariableNaming: | ||
active: true | ||
variablePattern: '[a-z][A-Za-z0-9]*' | ||
performance: | ||
active: true | ||
ArrayPrimitive: | ||
active: true | ||
ForEachOnRange: | ||
active: true | ||
SpreadOperator: | ||
active: true | ||
UnnecessaryTemporaryInstantiation: | ||
active: true | ||
potential-bugs: | ||
active: true | ||
EqualsAlwaysReturnsTrueOrFalse: | ||
active: true | ||
EqualsWithHashCodeExist: | ||
active: true | ||
ExplicitGarbageCollectionCall: | ||
active: true | ||
HasPlatformType: | ||
active: true | ||
InvalidRange: | ||
active: true | ||
UnreachableCatchBlock: | ||
active: true | ||
UnreachableCode: | ||
active: true | ||
UnsafeCallOnNullableType: | ||
active: true | ||
UnsafeCast: | ||
active: true | ||
WrongEqualsTypeParameter: | ||
active: true |
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.