8000 chore: add JetBrains auto-approval compliance linter (#139) · coder/coder-jetbrains-toolbox@6ca08df · GitHub
[go: up one dir, main page]

Skip to content

Commit 6ca08df

Browse files
chore: add JetBrains auto-approval compliance linter (#139)
Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com> Co-authored-by: matifali <10648092+matifali@users.noreply.github.com>
1 parent abdf6d8 commit 6ca08df

File tree

5 files changed

+379
-1
lines changed

5 files changed

+379
-1
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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 gradlew executable
35+
run: chmod +x ./gradlew
36+
37+
- name: Run JetBrains Compliance Checks
38+
run: |
39+
echo "Running JetBrains auto-approval compliance checks with detekt..."
40+
./gradlew detekt
41+
42+
- name: Upload detekt reports
43+
uses: actions/upload-artifact@v4
44+
if: always()
45+
with:
46+
name: detekt-reports
47+
path: |
48+
build/reports/detekt/
49+
retention-days: 30
50+
6D47 51+
- name: Comment PR with compliance status
52+
if: github.event_name == 'pull_request' && failure()
53+
uses: actions/github-script@v7
54+
with:
55+
script: |
56+
github.rest.issues.createComment({
57+
issue_number: context.issue.number,
58+
owner: context.repo.owner,
59+
repo: context.repo.repo,
60+
body: '⚠️ **JetBrains Auto-Approval Compliance Check Failed**\n\n' +
61+
'This PR contains code that violates JetBrains auto-approval requirements:\n\n' +
62+
'- ❌ Do **not** use forbidden Kotlin experimental APIs\n' +
63+
'- ❌ Do **not** add lambdas, handlers, or class handles to Java runtime hooks\n' +
64+
'- ❌ Do **not** create threads manually (use coroutines or ensure cleanup in `CoderRemoteProvider#close()`)\n' +
65+
'- ❌ Do **not** bundle libraries already provided by Toolbox\n' +
66+
'- ❌ Do **not** perform ill-intentioned actions\n\n' +
67+
'Please check the workflow logs for detailed violations and fix them before merging.'
68+
})

JETBRAINS_COMPLIANCE.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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 with Detekt
29+
30+
The primary compliance checking is done using Detekt with custom configuration in `detekt.yml`:
31+
32+
```bash
33+
./gradlew detekt
34+
```
35+
36+
This configuration includes JetBrains-specific rules that check for:
37+
- **ForbiddenAnnotation**: Detects forbidden experimental API usage
38+
- **ForbiddenMethodCall**: Detects Java runtime hooks and manual thread creation
39+
- **ForbiddenImport**: Detects potentially bundled libraries
40+
- **Standard code quality rules**: Complexity, naming, performance, etc.
41+
42+
43+
44+
## CI/CD Integration
45+
46+
The GitHub Actions workflow `.github/workflows/jetbrains-compliance.yml` runs compliance checks on every PR and push.
47+
48+
## Running Locally
49+
50+
```bash
51+
# Run JetBrains compliance and code quality check
52+
./gradlew detekt
53+
54+
# View HTML report
55+
open build/reports/detekt/detekt.html
56+
```
57+
58+
59+
60+
## Understanding Results
61+
62+
### Compliance Check Results
63+
64+
- **✅ No critical violations**: Code complies with JetBrains requirements
65+
- **❌ Critical violations**: Must be fixed before auto-approval
66+
- **⚠️ Warnings**: Should be reviewed but may be acceptable
67+
68+
### Common Warnings
69+
70+
1. **Manual thread creation**: If you see warnings about thread creation:
71+
- Prefer coroutines: `coroutineScope.launch { ... }`
72+
- If using libraries with threads, ensure cleanup in `close()`
73+
74+
2. **Library imports**: If you see warnings about library imports:
75+
- Verify the library isn't bundled in the final plugin
76+
- Check that Toolbox doesn't already provide the library
77+
78+
3. **GlobalScope usage**: If you see warnings about `GlobalScope`:
79+
- Use the coroutine scope provided by Toolbox instead
80+
81+
## Resources
82+
83+
- [JetBrains Toolbox Plugin Development](https://plugins.jetbrains.com/docs/toolbox/)
84+
- [Detekt Documentation](https://detekt.dev/)
85+
- [Kotlin Coroutines Guide](https://kotlinlang.org/docs/coroutines-guide.html)

build.gradle.kts

Lines changed: 19 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,24 @@ tasks.test {
110111
useJUnitPlatform()
111112
}
112113

114+
// Detekt configuration for JetBrains compliance and code quality
115+
detekt {
116+
config.setFrom("$projectDir/detekt.yml")
117+
buildUponDefaultConfig = true
118+
allRules = false
119+
}
120+
121+
// Configure detekt for JetBrains compliance and code quality
122+
tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
123+
jvmTarget = "21"
F987 124+
reports {
125+
html.required.set(true)
126+
xml.required.set(true)
127+
}
128+
// Fail build on detekt issues for JetBrains compliance
129+
ignoreFailures = false
130+
}
131+
113132

114133
tasks.jar {
115134
archiveBaseName.set(extension.id)

detekt.yml

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Detekt configuration for JetBrains Toolbox Plugin Auto-Approval Compliance
2+
# Based on clarified requirements from JetBrains team
3+
4+
build:
5+
maxIssues: 1000 # Allow many issues for code quality reporting
6+
excludeCorrectable: false
7+
8+
config:
9+
validation: true
10+
warningsAsErrors: false # Don't treat warnings as errors
11+
checkExhaustiveness: false
12+
13+
# CRITICAL: JetBrains Compliance Rules using detekt built-in rules
14+
style:
15+
active: true
16+
17+
# JetBrains Auto-Approval Compliance: Forbidden experimental annotations
18+
ForbiddenAnnotation:
19+
active: true
20+
annotations:
21+
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed'
22+
value: 'kotlin.ExperimentalStdlibApi'
23+
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed'
24+
value: 'kotlin.ExperimentalUnsignedTypes'
25+
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed'
26+
value: 'kotlin.contracts.ExperimentalContracts'
27+
- reason: 'Forbidden for JetBrains auto-approval: Core Kotlin experimental APIs are not allowed'
28+
value: 'kotlin.experimental.ExperimentalTypeInference'
29+
- reason: 'Forbidden for JetBrains auto-approval: Internal coroutines APIs should be avoided'
30+
value: 'kotlinx.coroutines.InternalCoroutinesApi'
31+
- reason: 'Forbidden for JetBrains auto-approval: Experimental time APIs are not allowed'
32+
value: 'kotlin.time.ExperimentalTime'
33+
# Note: ExperimentalCoroutinesApi, DelicateCoroutinesApi, FlowPreview are acceptable
34+
# based on JetBrains feedback about select/onTimeout being OK
35+
36+
# JetBrains Auto-Approval Compliance: Forbidden method calls
37+
ForbiddenMethodCall:
38+
active: true
39+
methods:
40+
# Java runtime hooks - forbidden
41+
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed'
42+
value: 'java.lang.Runtime.addShutdownHook'
43+
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed'
44+
value: 'java.lang.System.setSecurityManager'
45+
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed'
46+
value: 'java.lang.Thread.setUncaughtExceptionHandler'
47+
- reason: 'Forbidden for JetBrains auto-approval: Java runtime hooks are not allowed'
48+
value: 'java.lang.Thread.setDefaultUncaughtExceptionHandler'
49+
# Manual thread creation - warnings (allowed with proper cleanup)
50+
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()'
51+
value: 'java.lang.Thread.<init>'
52+
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()'
53+
value: 'java.util.concurrent.Executors.newFixedThreadPool'
54+
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()'
55+
value: 'java.util.concurrent.Executors.newCachedThreadPool'
56+
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()'
57+
value: 'java.util.concurrent.Executors.newSingleThreadExecutor'
58+
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()'
59+
value: 'java.util.concurrent.CompletableFuture.runAsync'
60+
- reason: 'Warning for JetBrains auto-approval: Manual thread creation detected. Consider using coroutineScope.launch or ensure proper cleanup in CoderRemoteProvider#close()'
61+
value: 'java.util.concurrent.CompletableFuture.supplyAsync'
62+
63+
# JetBrains Auto-Approval Compliance: Forbidden imports
64+
ForbiddenImport:
65+
active: true
66+
imports:
67+
# Potentially bundled libraries - warnings
68+
- reason: 'Warning for JetBrains auto-approval: Ensure slf4j is not bundled - it is provided by Toolbox'
69+
value: 'org.slf4j.*'
70+
- reason: 'Warning for JetBrains auto-approval: Ensure annotations library is not bundled - it is provided by Toolbox'
71+
value: 'org.jetbrains.annotations.*'
72+
# Runtime hook classes - forbidden
73+
- reason: 'Forbidden for JetBrains auto-approval: Runtime hook classes are not allowed'
74+
value: 'java.lang.Runtime'
75+
- reason: 'Forbidden for JetBrains auto-approval: Security manager modifications are not allowed'
76+
value: 'java.security.SecurityManager'
77+
78+
# Other important style rules
79+
MagicNumber:
80+
active: true
81+
ignoreNumbers:
82+
- '-1'
83+
- '0'
84+
- '1'
85+
- '2'
86+
ignoreHashCodeFunction: true
87+
ignorePropertyDeclaration: false
88+
ignoreLocalVariableDeclaration: false
89+
ignoreConstantDeclaration: true
90+
ignoreCompanionObjectPropertyDeclaration: true
91+
ignoreAnnotation: false
92+
ignoreNamedArgument: true
93+
ignoreEnums: false
94+
ignoreRanges: false
95+
ignoreExtensionFunctions: true
96+
97+
MaxLineLength:
98+
active: true
99+
maxLineLength: 120
100+
excludePackageStatements: true
101+
excludeImportStatements: true
102+
excludeCommentStatements: false
103+
104+
NewLineAtEndOfFile:
105+
active: true
106+
107+
WildcardImport:
108+
active: true
109+
110+
# Essential built-in rules for basic code quality
111+
complexity:
112+
active: true
113+
CyclomaticComplexMethod:
114+
active: true
115+
threshold: 15
116+
LongMethod:
117+
active: true
118+
threshold: 60
119+
LongParameterList:
120+
active: true
121+
functionThreshold: 6
122+
constructorThreshold: 7
123+
NestedBlockDepth:
124+
active: true
125+
threshold: 4
126+
127+
coroutines:
128+
active: true
129+
GlobalCoroutineUsage:
130+
active: true
131+
RedundantSuspendModifier:
132+
active: true
133+
SleepInsteadOfDelay:
134+
active: true
135+
136+
exceptions:
137+
active: true
138+
ExceptionRaisedInUnexpectedLocation:
139+
active: true
140+
ObjectExtendsThrowable:
141+
active: true
142+
PrintStackTrace:
143+
active: true
144+
ReturnFromFinally:
145+
active: true
146+
SwallowedException:
147+
active: true
148+
ThrowingExceptionFromFinally:
149+
active: true
150+
ThrowingExceptionsWithoutMessageOrCause:
151+
active: true
152+
TooGenericExceptionCaught:
153+
active: true
154+
TooGenericExceptionThrown:
155+
active: true
156+
157+
naming:
158+
active: true
159+
ClassNaming:
160+
active: true
161+
classPattern: '[A-Z][a-zA-Z0-9]*'
162+
FunctionNaming:
163+
active: true
164+
functionPattern: '[a-z][a-zA-Z0-9]*'
165+
PackageNaming:
166+
active: true
167+
packagePattern: '[a-z]+(\.?[a-z][A-Za-z0-9]*)*'
168+
VariableNaming:
169+
active: true
170+
variablePattern: '[a-z][A-Za-z0-9]*'
171+
172+
performance:
173+
active: true
174+
ArrayPrimitive:
175+
active: true
176+
ForEachOnRange:
177+
active: true
178+
SpreadOperator:
179+
active: true
180+
UnnecessaryTemporaryInstantiation:
181+
active: true
182+
183+
potential-bugs:
184+
active: true
185+
EqualsAlwaysReturnsTrueOrFalse:
186+
active: true
187+
EqualsWithHashCodeExist:
188+
active: true
189+
ExplicitGarbageCollectionCall:
190+
active: true
191+
HasPlatformType:
192+
active: true
193+
InvalidRange:
194+
active: true
195+
UnreachableCatchBlock:
196+
active: true
197+
UnreachableCode:
198+
active: true
199+
UnsafeCallOnNullableType:
200+
active: true
201+
UnsafeCast:
202+
active: true
203+
WrongEqualsTypeParameter:
204+
active: true

0 commit comments

Comments
 (0)
0