Qodana Cheat Sheet
Overview
Qodana is JetBrains' code quality platform that brings the smart code inspections from JetBrains IDEs to your CI/CD pipeline. It provides comprehensive static analysis, security vulnerability detection, and code quality metrics for multiple programming languages with deep integration into development workflows.
⚠️ Note: Free tier available with limitations. Paid plans start at $0.50 per 1000 lines of code per month.
Getting Started
Installation Methods
# Docker (Recommended)
docker pull jetbrains/qodana-jvm
docker pull jetbrains/qodana-js
docker pull jetbrains/qodana-python
docker pull jetbrains/qodana-php
docker pull jetbrains/qodana-dotnet
# CLI Installation
# Linux/macOS
curl -fsSL https://jb.gg/qodana-cli/install | bash
# Windows (PowerShell)
iex "& { $(irm https://jb.gg/qodana-cli/install-ps1) }"
# Homebrew (macOS)
brew install jetbrains/utils/qodana
# Snap (Linux)
sudo snap install qodana
Quick Start
# Initialize Qodana in project
qodana init
# Run analysis with Docker
docker run --rm -it \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-jvm
# Run with CLI
qodana scan --project-dir . --results-dir ./qodana-results
Project Setup
# qodana.yaml configuration file
version: "1.0"
profile:
name: qodana.recommended
include:
- name: All
exclude:
- name: "Unused import"
- name: "Spelling"
projectJDK: "11"
bootstrap: |
set -eu
echo "Bootstrap script execution"
./gradlew build -x test
Language Support
JVM Languages (Java, Kotlin, Scala)
# Qodana JVM Linter
docker run --rm -it \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-jvm
# Supported build tools:
# - Maven
# - Gradle
# - SBT
# - Ant
# Java-specific inspections:
# - Code style violations
# - Null pointer analysis
# - Resource leak detection
# - Performance issues
# - Security vulnerabilities
JavaScript/TypeScript
# Qodana JS Linter
docker run --rm -it \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-js
# Supported frameworks:
# - React
# - Vue.js
# - Angular
# - Node.js
# - Express
# JS/TS inspections:
# - ESLint integration
# - TypeScript compiler checks
# - React-specific issues
# - Async/await problems
# - Performance optimizations
Python
# Qodana Python Linter
docker run --rm -it \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-python
# Python features:
# - PEP 8 compliance
# - Type checking with mypy
# - Security vulnerability detection
# - Code complexity analysis
# - Import optimization
PHP
# Qodana PHP Linter
docker run --rm -it \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-php
# PHP inspections:
# - PSR compliance
# - Security vulnerabilities
# - Performance issues
# - Type safety
# - Framework-specific checks
.NET (C#, VB.NET, F#)
# Qodana .NET Linter
docker run --rm -it \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-dotnet
# .NET features:
# - Roslyn analyzer integration
# - Security rule checks
# - Performance optimizations
# - Code style enforcement
# - Framework compatibility
Configuration
Basic Configuration
# qodana.yaml - Basic setup
version: "1.0"
profile:
name: qodana.recommended
include:
- name: All
exclude:
- name: "Unused import"
- name: "Spelling"
- name: "Typo"
Advanced Configuration
# qodana.yaml - Advanced setup
version: "1.0"
profile:
name: qodana.recommended
path: .qodana/profiles/custom.xml
include:
- name: "Code style issues"
- name: "Probable bugs"
- name: "Performance"
exclude:
- name: "Spelling"
- name: "Unused import"
- path: "src/test/**"
- path: "**/*.generated.js"
projectJDK: "11"
bootstrap: |
set -eu
echo "Installing dependencies..."
npm install
./gradlew build -x test
failThreshold: 0
Profile Customization
<!-- .qodana/profiles/custom.xml -->
<profile version="1.0">
<option name="myName" value="Custom Profile" />
<inspection_tool class="UnusedDeclaration" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="true" />
<inspection_tool class="JSUnusedLocalSymbols" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
Environment Variables
# Common environment variables
export QODANA_TOKEN="your-token-here"
export QODANA_PROJECT_ID="project-id"
export QODANA_BRANCH="main"
export QODANA_REVISION="HEAD"
# Docker environment variables
docker run --rm -it \
-e QODANA_TOKEN=$QODANA_TOKEN \
-e QODANA_PROJECT_ID=$QODANA_PROJECT_ID \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-jvm
CLI Commands
Basic Commands
# Initialize project
qodana init
# Scan project
qodana scan
# Scan with specific linter
qodana scan --linter jetbrains/qodana-jvm
# Scan with custom config
qodana scan --config ./custom-qodana.yaml
# Show scan results
qodana show
# View help
qodana --help
qodana scan --help
Advanced CLI Usage
# Scan with custom parameters
qodana scan \
--project-dir /path/to/project \
--results-dir /path/to/results \
--cache-dir /path/to/cache \
--linter jetbrains/qodana-jvm \
--config qodana.yaml \
--property=idea.headless.enable.statistics=false
# Scan specific files/directories
qodana scan --include-paths "src/main/**,lib/**"
qodana scan --exclude-paths "src/test/**,**/*.generated.*"
# Set fail threshold
qodana scan --fail-threshold 10
# Generate SARIF report
qodana scan --sarif-report results.sarif
# Upload results to Qodana Cloud
qodana scan --upload-result
Result Management
# View results in browser
qodana show --port 8080
# Export results
qodana export --format json --output results.json
qodana export --format sarif --output results.sarif
# Compare results between branches
qodana diff --base main --target feature-branch
# Generate baseline
qodana scan --baseline qodana.sarif.json
CI/CD Integration
GitHub Actions
# .github/workflows/qodana.yml
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2023.2
with:
args: --fail-threshold,0
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
GitLab CI
# .gitlab-ci.yml
qodana:
stage: test
image: jetbrains/qodana-jvm:latest
script:
- qodana --results-dir $CI_PROJECT_DIR/qodana-results
artifacts:
reports:
codequality: qodana-results/qodana.json
paths:
- qodana-results/
expire_in: 1 week
only:
- merge_requests
- main
Jenkins Pipeline
pipeline {
agent any
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Qodana Analysis') {
steps {
script {
docker.image('jetbrains/qodana-jvm:latest').inside {
sh '''
qodana --results-dir ./qodana-results \
--fail-threshold 0 \
--sarif-report qodana.sarif
'''
}
}
}
post {
always {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'qodana-results',
reportFiles: 'report.html',
reportName: 'Qodana Report'
])
recordIssues(
enabledForFailure: true,
tools: [sarif(pattern: 'qodana.sarif')]
)
}
}
}
}
}
Azure DevOps
# azure-pipelines.yml
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
displayName: 'Run Qodana Analysis'
inputs:
command: 'run'
arguments: >
--rm
-v $(Build.SourcesDirectory):/data/project/
-v $(Build.ArtifactStagingDirectory)/qodana-results:/data/results/
jetbrains/qodana-jvm
- task: PublishBuildArtifacts@1
displayName: 'Publish Qodana Results'
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)/qodana-results'
artifactName: 'qodana-results'
Quality Gates and Thresholds
Fail Threshold Configuration
# Set fail threshold (number of problems)
qodana scan --fail-threshold 10
# Fail on any critical issues
qodana scan --fail-threshold 0 --severity critical
# Fail on new issues only
qodana scan --baseline qodana-baseline.sarif.json --fail-threshold 0
Quality Gate Profiles
# qodana.yaml with quality gates
version: "1.0"
profile:
name: qodana.recommended
failThreshold: 5
include:
- name: "Critical"
- name: "High"
exclude:
- name: "Low"
- name: "Informational"
Custom Quality Rules
<!-- Custom inspection profile -->
<profile version="1.0">
<option name="myName" value="Strict Quality Gate" />
<!-- Critical issues that should fail the build -->
<inspection_tool class="NullPointerException" enabled="true" level="ERROR" />
<inspection_tool class="SqlInjection" enabled="true" level="ERROR" />
<inspection_tool class="XssVulnerability" enabled="true" level="ERROR" />
<!-- High priority issues -->
<inspection_tool class="UnusedDeclaration" enabled="true" level="WARNING" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WARNING" />
<!-- Disabled for CI -->
<inspection_tool class="SpellCheckingInspection" enabled="false" />
<inspection_tool class="TodoComment" enabled="false" />
</profile>
Security Analysis
Security Vulnerability Detection
# Enable security inspections
qodana scan --include "Security"
# Focus on critical security issues
qodana scan \
--include "SQL injection" \
--include "XSS" \
--include "Path traversal" \
--include "Hardcoded credentials"
Common Security Issues Detected
// SQL Injection Detection
// ❌ Vulnerable code
String query = "SELECT * FROM users WHERE id = " + userId;
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);
// ✅ Fixed code
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, userId);
ResultSet rs = stmt.executeQuery();
// Hardcoded Credentials Detection
// ❌ Vulnerable code
String password = "admin123";
String apiKey = "sk-1234567890abcdef";
// ✅ Fixed code
String password = System.getenv("DB_PASSWORD");
String apiKey = System.getenv("API_KEY");
Security Baseline
# Create security baseline
qodana scan --include "Security" --baseline security-baseline.sarif.json
# Check only new security issues
qodana scan --baseline security-baseline.sarif.json --include "Security"
Performance Analysis
Performance Inspections
# Focus on performance issues
qodana scan --include "Performance"
# Common performance issues detected:
# - Inefficient string concatenation
# - Resource leaks
# - Unnecessary object creation
# - Inefficient collections usage
# - Database query optimization
Performance Examples
// String Concatenation Performance
// ❌ Inefficient
String result = "";
for (String item : items) {
result += item + ", ";
}
// ✅ Efficient
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item).append(", ");
}
String result = sb.toString();
// Resource Leak Detection
// ❌ Resource leak
FileInputStream fis = new FileInputStream("file.txt");
// Missing close() call
// ✅ Proper resource management
try (FileInputStream fis = new FileInputStream("file.txt")) {
// Use the stream
} // Automatically closed
Code Quality Metrics
Complexity Analysis
# Analyze code complexity
qodana scan --include "Complexity"
# Metrics collected:
# - Cyclomatic complexity
# - Cognitive complexity
# - Lines of code
# - Method length
# - Class size
Duplication Detection
# Detect code duplication
qodana scan --include "Code duplication"
# Configure duplication thresholds in qodana.yaml:
version: "1.0"
profile:
name: qodana.recommended
duplicates:
minTokens: 50
minLines: 10
Technical Debt Assessment
// Technical Debt Examples
// ❌ High technical debt
function processData(data) {
// TODO: Refactor this method
if (data) {
if (data.length > 0) {
for (let i = 0; i < data.length; i++) {
if (data[i].status === 'active') {
// Complex nested logic
if (data[i].type === 'premium') {
// More nesting...
}
}
}
}
}
}
// ✅ Refactored code
function processActiveData(data) {
if (!data?.length) return;
return data
.filter(item => item.status === 'active')
.map(item => processItem(item));
}
function processItem(item) {
return item.type === 'premium'
? processPremiumItem(item)
: processStandardItem(item);
}
Reporting and Visualization
HTML Reports
# Generate HTML report
qodana scan --show-report
# Custom report location
qodana scan --results-dir ./custom-results
qodana show --port 8080 --results-dir ./custom-results
SARIF Reports
# Generate SARIF report
qodana scan --sarif-report results.sarif
# Upload to GitHub Security tab
# (automatically done with GitHub Actions integration)
# Convert SARIF to other formats
qodana export --format json --input results.sarif --output results.json
Custom Report Templates
<!-- Custom HTML template -->
<!DOCTYPE html>
<html>
<head>
<title>Custom Qodana Report</title>
<style>
.critical { color: red; font-weight: bold; }
.high { color: orange; }
.medium { color: yellow; }
.low { color: green; }
</style>
</head>
<body>
<h1>Code Quality Report</h1>
<div id="summary">
<!-- Summary statistics -->
</div>
<div id="issues">
<!-- Issue details -->
</div>
</body>
</html>
IDE Integration
IntelliJ IDEA Integration
# Qodana plugin for IntelliJ IDEA
# 1. Install Qodana plugin from marketplace
# 2. Configure Qodana settings
# 3. Run analysis from IDE
# 4. View results in tool window
# Plugin features:
# - Run Qodana analysis locally
# - View results in IDE
# - Navigate to issues
# - Configure inspection profiles
# - Integration with VCS
VS Code Integration
# Qodana extension for VS Code
# 1. Install Qodana extension
# 2. Configure workspace settings
# 3. Run analysis command
# 4. View results in problems panel
# Extension commands:
# - Qodana: Scan Project
# - Qodana: Show Results
# - Qodana: Configure
# - Qodana: Upload Results
Command Line Integration
# IDE-like experience in terminal
qodana scan --ide-like
# Open results in default browser
qodana show --open
# Watch mode for development
qodana scan --watch --fail-threshold 0
Advanced Features
Baseline Management
# Create baseline from current state
qodana scan --baseline qodana-baseline.sarif.json
# Update baseline
qodana scan --update-baseline qodana-baseline.sarif.json
# Use baseline to check only new issues
qodana scan --baseline qodana-baseline.sarif.json --fail-threshold 0
Custom Inspections
// Custom inspection example
class CustomInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : JavaElementVisitor() {
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
super.visitMethodCallExpression(expression)
if (isDeprecatedMethod(expression)) {
holder.registerProblem(
expression,
"Use of deprecated method",
ProblemHighlightType.WARNING
)
}
}
}
}
private fun isDeprecatedMethod(expression: PsiMethodCallExpression): Boolean {
// Custom logic to detect deprecated methods
return false
}
}
License Compliance
# License detection and compliance
qodana scan --include "License"
# Configure allowed licenses
version: "1.0"
profile:
name: qodana.recommended
licenseRules:
allowed:
- "MIT"
- "Apache-2.0"
- "BSD-3-Clause"
prohibited:
- "GPL-3.0"
- "AGPL-3.0"
Troubleshooting
Common Issues
# Out of memory errors
docker run --rm -it \
-m 4g \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-jvm
# Permission issues
sudo chown -R $USER:$USER qodana-results/
# Network connectivity issues
docker run --rm -it \
--network host \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-jvm
Performance Optimization
# Optimize for large projects
qodana scan \
--cache-dir ./qodana-cache \
--exclude-paths "node_modules/**,target/**,build/**" \
--include "Critical,High"
# Parallel processing
docker run --rm -it \
--cpus="4" \
-m 8g \
-v $(pwd):/data/project/ \
-v $(pwd)/qodana-results:/data/results/ \
jetbrains/qodana-jvm
Debug Mode
# Enable debug logging
qodana scan --log-level debug
# Verbose output
qodana scan --verbose
# Save logs
qodana scan --log-file qodana.log
Best Practices
Configuration Management
# Version control qodana.yaml
# - Keep configuration in repository
# - Use environment-specific configs
# - Document configuration changes
# - Review configuration in PRs
# Example structure:
.qodana/
├── qodana.yaml # Main configuration
├── profiles/
│ ├── strict.xml # Strict quality profile
│ └── relaxed.xml # Relaxed profile for legacy code
├── baselines/
│ ├── main.sarif.json # Main branch baseline
│ └── legacy.sarif.json # Legacy code baseline
└── scripts/
└── bootstrap.sh # Setup script
CI/CD Best Practices
# Gradual adoption strategy:
# 1. Start with baseline to avoid failing builds
# 2. Gradually reduce fail threshold
# 3. Focus on critical and high-priority issues first
# 4. Regular baseline updates
# 5. Team training and adoption
# Performance considerations:
# - Use caching between builds
# - Exclude unnecessary files/directories
# - Run on appropriate hardware
# - Use incremental analysis when possible
Team Adoption
# Team onboarding:
# 1. Introduce Qodana gradually
# 2. Provide training on code quality
# 3. Set up IDE integration
# 4. Regular review of quality metrics
# 5. Celebrate improvements
# Quality culture:
# - Make quality metrics visible
# - Regular quality reviews
# - Code quality champions
# - Continuous improvement mindset