name: checkstyle description: Check and fix Checkstyle violations in jhelm modules argument-hint: [module] allowed-tools: Bash(./mvnw *), Bash(python3 *)
Check and Fix Checkstyle Violations
Configuration
- Plugin:
maven-checkstyle-plugin3.6.0 withspring-javaformat-checkstyle0.0.43 - Suppressions:
checkstyle-suppressions.xmlin project root - Violations fail the build at
validatephase (before compile)
Step 1: Check violations
All modules:
./mvnw validate 2>&1 | grep -E "violations|ERROR.*\.java"
Specific module (e.g., /checkstyle jhelm-core):
./mvnw validate -pl $ARGUMENTS 2>&1 | grep "^\[ERROR\]"
Step 2: Auto-format first
Always run spring-javaformat:apply before checking violations — it fixes indentation/spacing automatically:
./mvnw spring-javaformat:apply
Step 3: Fix remaining violations with Python script
Save this script as /tmp/fix_violations.py and run for any module:
#!/usr/bin/env python3
"""Fix checkstyle violations: SpringCatch, NeedBraces, SpringLambda, SpringTernary,
AvoidStarImport, UnusedImports, AnnotationUseStyle."""
import re, os
from pathlib import Path
JAVA_KEYWORDS = {
'abstract','assert','boolean','break','byte','case','catch','char','class',
'const','continue','default','do','double','else','enum','extends','final',
'finally','float','for','goto','if','implements','import','instanceof','int',
'interface','long','native','new','package','private','protected','public',
'return','short','static','strictfp','super','switch','synchronized','this',
'throw','throws','transient','try','void','volatile','while',
'null','true','false','var','record','sealed','permits','yield',
}
STATIC_STAR_IMPORTS = {
'org.junit.jupiter.api.Assertions': [
'assertEquals','assertNotEquals','assertTrue','assertFalse','assertNull',
'assertNotNull','assertThrows','assertDoesNotThrow','assertSame','assertNotSame',
'fail','assertAll','assertArrayEquals','assertIterableEquals','assertInstanceOf',
],
'org.mockito.Mockito': [
'mock','when','verify','doReturn','doThrow','doNothing','doAnswer','spy',
'times','never','atLeast','atLeastOnce','atMost','reset',
'verifyNoInteractions','verifyNoMoreInteractions','lenient','inOrder',
'mockConstruction','mockStatic','any','anyString','anyInt','anyLong',
'anyBoolean','anyList','anyMap','anySet','eq','isNull','isNotNull','same','argThat',
],
'org.mockito.ArgumentMatchers': [
'any','anyString','anyInt','anyLong','anyBoolean','anyList','anyMap',
'anySet','eq','isNull','isNotNull','same','argThat',
],
}
REGULAR_STAR_IMPORTS = {
'java.util': ['List','ArrayList','Map','HashMap','LinkedHashMap','TreeMap',
'Set','HashSet','LinkedHashSet','TreeSet','Collections','Arrays','Optional',
'Iterator','Queue','Deque','LinkedList','Objects','Comparator','UUID',],
'java.io': ['File','InputStream','OutputStream','IOException','FileInputStream',
'FileOutputStream','BufferedInputStream','BufferedReader','BufferedWriter',
'StringWriter','StringReader','Closeable','ByteArrayInputStream','ByteArrayOutputStream',],
'org.alexmond.jhelm.core': ['Engine','Chart','ChartMetadata','ChartLoader',
'ChartLock','Dependency','DependencyResolver','Release','RepositoryConfig',
'KubeService','ListAction','InstallAction','UpgradeAction','UninstallAction',
'RollbackAction','GetAction','StatusAction','HistoryAction','ShowAction',
'TemplateAction','CreateAction','RepoManager','RegistryManager',
'HelmChartTemplates','CoreConfig',],
'io.kubernetes.client.openapi.models': None, # auto-detect V1* classes
}
def fix_spring_catch(content):
lines = content.split('\n')
result, i = [], 0
while i < len(lines):
line = lines[i]
m = re.match(r'^(\s*catch\s*\(\s*(?:\w+(?:\s*\|\s*\w+)*)\s+)(e)(\s*\)\s*)\{(.*)$', line)
if m:
result.append(m.group(1) + 'ex' + m.group(3) + '{' + m.group(4))
i += 1
depth = 1
while i < len(lines) and depth > 0:
bl = lines[i]
for ch in bl:
if ch == '{': depth += 1
elif ch == '}': depth -= 1
if depth > 0:
bl = re.sub(r'\be\.', 'ex.', bl)
bl = re.sub(r'(?<=[,\s(])\be\b(?=[,\s);])', 'ex', bl)
bl = re.sub(r'\bthrow\s+e\s*;', 'throw ex;', bl)
result.append(bl)
i += 1
else:
result.append(line)
i += 1
return '\n'.join(result)
def count_net_parens(line):
depth, in_str, str_ch, esc = 0, False, None, False
for ch in line:
if esc: esc = False; continue
if ch == '\\': esc = True; continue
if in_str:
if ch == str_ch: in_str = False
else:
if ch in ('"', "'"): in_str, str_ch = True, ch
elif ch == '(': depth += 1
elif ch == ')': depth -= 1
return depth
def fix_need_braces(content):
lines = content.split('\n')
result, i = [], 0
CTRL = re.compile(r'^\s*(?:(?:else\s+)?if|for|while)\s*\(')
ELSE = re.compile(r'^(\s*)else\s*$')
while i < len(lines):
line = lines[i]
stripped = line.rstrip()
em = ELSE.match(stripped)
if em and i + 1 < len(lines):
indent = em.group(1)
ns = lines[i+1].strip()
if ns and not ns.startswith(('{','//','*','@')):
result.extend([line + ' {', lines[i+1], indent + '}'])
i += 2; continue
if CTRL.match(stripped):
net = count_net_parens(stripped)
if net == 0 and not stripped.endswith('{') and stripped.endswith(')'):
if i + 1 < len(lines):
ns = lines[i+1].strip()
if ns and not ns.startswith(('{','//','*','@')) and not CTRL.match(ns):
indent = re.match(r'^(\s*)', line).group(1)
result.extend([line + ' {', lines[i+1], indent + '}'])
i += 2; continue
result.append(line); i += 1
return '\n'.join(result)
def fix_lambda_blocks(content):
lines = content.split('\n')
result, i = [], 0
while i < len(lines):
line = lines[i]
m = re.match(r'^(.*?)\s*->\s*\{\s*$', line.rstrip())
if m and i + 2 < len(lines):
body = lines[i+1].strip()
close = lines[i+2].strip()
if re.match(r'^\}\s*[\);]*\s*$', close) and body.endswith(';'):
val = body[7:-1] if body.startswith('return ') else body[:-1]
suffix = close[1:]
result.append(f'{m.group(1)} -> {val}{suffix}')
i += 3; continue
result.append(line); i += 1
return '\n'.join(result)
def fix_lambda_parens(content):
def fix_line(line):
def replacer(m):
ident = m.group(1)
if ident in JAVA_KEYWORDS: return m.group(0)
pre = line[:m.start(1)]
if re.search(r'\bcase\b', pre): return m.group(0)
if pre.rstrip() and re.search(r'\b[A-Z]\w*(?:<[^>]*>)?\s*$', pre.rstrip()): return m.group(0)
if line[m.end(1):m.end(1)+1] == ')': return m.group(0)
return f'({ident}) ->'
return re.sub(r'\b([a-zA-Z_]\w*)\s+->', replacer, line)
return '\n'.join(fix_line(l) for l in content.split('\n'))
def fix_spring_ternary(content):
def fix_line(line):
if '?' not in line or line.strip().startswith(('//','*')): return line
# != null
line = re.sub(r'(?<!\()(\b[\w][\w.()]*(?:\(\))?)\s+!=\s+null\s+\?(?!\s*\))',
r'(\1 != null) ?', line)
# == null with return (swap branches)
def swap(m):
p,v,tb,eb = m.group(1),m.group(2),m.group(3).strip(),m.group(4).strip()
sfx = ';' if eb.endswith(';') else ''
if sfx: eb = eb[:-1].rstrip()
return f'{p}({v} != null) ? {eb} : {tb}{sfx}'
line = re.sub(r'(\breturn\s+)(\w+)\s+==\s+null\s+\?\s+([\w".\(\)]+)\s*:\s*([\w".\(\)]+\s*;?)', swap, line)
# instanceof
line = re.sub(r'(?<!\()(\b[\w][\w.]*)\s+(instanceof\s+[\w<>?.]+(?:\s+\w+)?)\s+\?(?!\s*\))',
r'(\1 \2) ?', line)
# simple comparison <
line = re.sub(r'(?<!\()(\b\w+)\s+(<)\s+([\w.]+)\s+\?(?!\s*\))', r'(\1 \2 \3) ?', line)
return line
return '\n'.join(fix_line(l) for l in content.split('\n'))
def fix_star_imports(content):
lines = content.split('\n')
imp_lines = [i for i,l in enumerate(lines) if l.strip().startswith('import ')]
if not imp_lines: return content
s, e = imp_lines[0], imp_lines[-1]
body = '\n'.join(lines[:s] + lines[e+1:])
new_imps = []
for i in range(s, e+1):
stripped = lines[i].strip()
ms = re.match(r'^import\s+static\s+([\w.]+)\.\*\s*;', stripped)
if ms:
pkg = ms.group(1)
if pkg in STATIC_STAR_IMPORTS:
for m in STATIC_STAR_IMPORTS[pkg]:
if re.search(r'\b' + re.escape(m) + r'\s*\(', body):
new_imps.append(f'import static {pkg}.{m};')
else: new_imps.append(lines[i])
continue
mr = re.match(r'^import\s+([\w.]+)\.\*\s*;', stripped)
if mr:
pkg = mr.group(1)
classes = REGULAR_STAR_IMPORTS.get(pkg)
if classes is None and pkg == 'io.kubernetes.client.openapi.models':
used = sorted(set(re.findall(r'\bV1[A-Z]\w+\b', body)))
for cls in used: new_imps.append(f'import {pkg}.{cls};')
elif classes is not None:
for cls in classes:
if re.search(r'\b' + re.escape(cls) + r'\b', body):
new_imps.append(f'import {pkg}.{cls};')
else: new_imps.append(lines[i])
continue
new_imps.append(lines[i])
seen, deduped = set(), []
for imp in new_imps:
if imp.strip() not in seen:
seen.add(imp.strip()); deduped.append(imp.strip())
return '\n'.join(lines[:s] + deduped + lines[e+1:])
def process(src_dirs):
for src_dir in src_dirs:
for root, dirs, files in os.walk(src_dir):
dirs[:] = [d for d in dirs if not d.startswith('.')]
for fname in files:
if not fname.endswith('.java'): continue
fp = os.path.join(root, fname)
with open(fp) as f: orig = f.read()
c = orig
c = fix_spring_catch(c)
c = fix_need_braces(c)
c = fix_lambda_blocks(c)
c = fix_lambda_parens(c)
c = fix_spring_ternary(c)
if '.*' in c: c = fix_star_imports(c)
if c != orig:
with open(fp, 'w') as f: f.write(c)
print(f' Fixed: {fp}')
if __name__ == '__main__':
base = Path('/Users/alex.mondshain/claude/jhelm')
process([str(base / m / 'src') for m in ['jhelm-core','jhelm-kube','jhelm-app','jhelm-gotemplate']])
Step 4: Handle special violations manually
| Violation | Fix |
|-----------|-----|
| InnerTypeLast | Move inner classes AFTER all methods |
| SpringHideUtilityClassConstructor | Add private Constructor() {} AND make class final |
| FinalClass | Add final to class declaration |
| NestedIfDepth > 3 | Extract helper method or add to checkstyle-suppressions.xml |
| SpringMethodVisibility (interface impl) | Add suppression to checkstyle-suppressions.xml |
| AnnotationUseStyle trailing comma | Remove , before }) in @CsvSource etc. |
Step 5: Re-format and validate
./mvnw spring-javaformat:apply && ./mvnw validate
Critical Pitfalls
- Lambda parens vs switch patterns:
case Boolean b -> b— do NOT add parens to switch pattern variables. Check if preceded by uppercase type name orcasekeyword. - Lambda parens vs switch default:
default -> value— do NOT add parens.defaultis a Java keyword. - Ternary in lambda body:
(args) -> condition ? a : b— the->arrow is a boundary; the condition starts AFTER->. Do NOT wrap the lambda params in the ternary condition. ==vs=in find_condition_start: When scanning backward for ternary condition start,==at positionihasline[i-1]='='— skip it. ANDline[i+1]='='also means it's==, skip.- Star import for
removeIf(r ->):ris preceded by(from the method call, NOT from lambda parens. Check if char AFTER identifier is)to detect already-parenthesized. - Missing imports after star import expansion: Manually check for
Base64,BufferedInputStream,VersionInfo, etc. that may not be in the predefined class lists.
Suppressions file (checkstyle-suppressions.xml)
Located at project root. Current suppressions:
SpringHeader,SpringTestFileName,JavadocPackage— project conventions differRegexpSinglelineJava— JUnit 5 assertions used instead of AssertJSpringImportOrder— handled byspring-javaformat:applyRequireThis— not enforced in this project- All
Javadoc*andSpringJavadoc— not required SpringMethodVisibilityforKpsComparisonTest.java— TrustManager interface requires publicNestedIfDepthforRepoManager.java— intentional deep YAML parsing
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.