You can configure Qodana using YAML or command-line (CLI) options.
Configuring Qodana via a YAML-formatted file typically named qodana.yaml and contained in the root directory of your project is suitable for settings that require lengthy commands. For example, inspection configuration, bootstrap, and other settings that are not convenient to configure otherwise. Once a YAML configuration is saved, you can reuse it across different instances of Qodana.
CLI options are suitable for immediate configuration of applications that run Qodana like the Docker engine, Qodana CLI, IDEs, and CI/CD tools. Besides that, some CLI options do not have YAML equivalents.
You can configure linter and quality gates via both YAML and CLI. In such cases, CLI options take precedence over their YAML equivalents if both methods are used.
YAML configuration
By default, this configuration capability will be referred to as the qodana.yaml configuration. To start, in the root directory of your project save the qodana.yaml file.
To override the qodana.yaml filename and its location, follow the recommendations of the Override YAML configuration file chapter.
The JSON schema for qodana.yaml is published in the SchemaStore project, which provides completion and basic validation in IDEs.
CLI options
You can configure Qodana using three types of CLI options as shown below.
Option type
Example
Requires the equal sign (=) between the option name and its argument
--property=idea.log.config.file=info.xml
Requires the space character () between the option name and its argument
--baseline /path/to/sarif/file
Requires no argument
--apply-fixes
Here are the examples that invoke all these option types:
During analyses, Qodana linters may report that some inspections cannot find classes, packages, files or cannot resolve references, although linters related to JVM, .NET, and Golang try to figure out the build system and project structure automatically.
When you run Qodana on a CI/CD platform, project dependencies are usually not installed automatically, so you may need to install the dependencies yourself. In these cases, Qodana needs a bit of help:
Install third-party packages or libraries
Run a program that sets up the build environment
These actions are carried out using the bootstrap key:
version: "1.0"
linter: <linter>
bootstrap: |+
set -eu
# For PHP projects that use Laravel:
#composer require --dev barryvdh/laravel-ide-helper
# For JavaScript projects that use Node.js:
#npm install
# For Python projects
#pip install -r requirements.txt
To be able to use syntax highlighting and validation in your IDE, you can create the prepare-qodana.sh shell script and save it in the root directory of your project:
#! /bin/sh
set -eu
# For PHP projects that use Laravel:
#composer require --dev barryvdh/laravel-ide-helper
# For JavaScript projects that use Node.js:
#npm install
Run the script in a Qodana Docker container using the bootstrap key:
bootstrap: sh ./prepare-qodana.sh
To run Qodana as the root user, you may need to invoke the --user=rootoption.
In CI/CD environments like GitLab CI/CD, you may need to prepare a complex environment before the analysis starts. Here is an example of a monorepo setup with a frontend directory (Node.js) and a backend directory (C#):
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --linter <linter>
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: |
--linter <linter>
--within-docker false
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Override YAML configuration file
Your project can have several Qodana configurations contained in YAML-formatted files. This comes in handy if you analyze monorepo projects or run a single CI job.
You can use the --config CLI option and a path to a file relative to the project root:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --config relative/path/to/config.yaml
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
In the runner configuration, find the Additional Qodana arguments field and specify the path to your custom configuration file:
--config relative/path/to/config.yaml
Inspection profile
By default, Qodana analyzes your code using the qodana.starter profile. You can switch to another existing profile using the recommendations from the Existing Qodana profiles chapter. To set up your own profile, use available configuration options described in the Custom profiles chapter.
YAML configuration
Using the profile YAML key, you can import an existing configuration from a dedicated file, as well as customize the configuration once it is imported:
Use the profile.path key to invoke an existing profile by its name, for example:
After you set up a profile in a dedicated file as described in the Inspection profiles section, you can invoke it in the qodana.yaml file using the profile.path key, for example:
Alternatively, you can use a profile name from a custom profile file. To do it, you will need to mount a profile file to Qodana.
qodana.starter
-p, --profile-path
The absolute path to the profile file.
None
--run-promo
Run promo inspections as a part of the qodana.starter profile
Enabled only if Qodana is configured for the qodana.starter profile, and the --run-promo true option is invoked
Below are the configuration snippets containing the -profile-name option for invoking a profile by its name, as well as the --profile-path CLI option for invoking a profile configuration from a dedicated file:
docker run \
-v $(pwd):/data/project/ \
-e QODANA_TOKEN="<cloud-project-token>" \
jetbrains/qodana-<image> \
-v <path-to-profile-file> \ # Mount the file containing custom profile
--profile-name qodana.recommended | <profile-name-from-file> \ # Existing profile name | Name from mounted file
--profile-path /data/project/myprofiles/<file-name> # Importing profile file
qodana scan \
-e QODANA_TOKEN="<cloud-project-token>" \
-v <path-to-profile-file> \ # Mount the file containing custom profile
--profile-name qodana.recommended | <profile-name-from-file> \ # Existing profile name | Name from mounted file
--profile-path /data/project/myprofiles/<file-name> # Importing profile file
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: |
-v <path-to-profile-file> # Mount the file containing custom profile
--profile-name qodana.recommended | <profile-name-from-file> # Existing profile name | Name from mounted file
--profile-path /data/project/myprofiles/<file-name> # Importing profile file
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Sanity problems refer to problems in the project configuration. By default, sanity inspections are enabled in Qodana. You can disable them using this snippet:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: |
--disable-sanity
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
In the runner configuration, find the Additional Qodana arguments field and specify the --disable-sanity option.
Inspections
Including inspections
You can tell Qodana to analyze files of a certain directory using an inspection that is not contained in the selected profile. This can be done on a per-analysis basis. To include all paths in a project into the inspection scope, omit the paths node. Information about inspection IDs is available on the Inspectopedia website.
In this YAML configuration example, the empty profile, which contains no inspections, is specified, and the SomeInspectionId inspection is explicitly included in the analysis scope for the tools directory. As a result, only the analysis performed by the SomeInspectionId inspection the tools directory contents will be included in the Qodana analysis scope.
Here, each inspections entry should reference an inspection registered in an active inspection profile, either enabled or disabled. If an inspection ID is not registered, it becomes silently ignored and no inspection is added.
For example, the profile.name: empty configuration implies that plugin-provided inspections like CppClangTidy* are not registered. To enable specific plugin inspections, you can start from an existing inspection profile like qodana.starterand suppress the inspections that you do not need. Also, add inspections using plugin-specific config like .clang-tidy in case of the C / C++ linter.
Excluding inspections
To disable inspections for a specific file, use the following YAML configuration:
This table lists the paths available in Qodana linters.
Path
Description
/data/project
Root directory of the project
/data/results
Directory to store analysis reports. It should be empty before running Qodana
/opt/idea
IDE distributive directory
/root/.config/idea
IDE configuration directory
/data/profile.xml
The default profile file containing the qodana.starter profile configuration. This file is used if a profile was not previously configured either via the CLI or the qodana.yaml file. See Order of resolving a profile for details
/data/project/.idea/inspectionProfiles/
Directory for binding profile files
/data/cache/.m2
Maven project dependencies
/root/.m2/
Directory for overriding the settings.xml configuration file for Maven
You can find below several examples of how these paths can be applied.
Configure Maven
In Maven, you can configure the source and target versions of the Java compiler. Qodana compares these values and selects the latest version. This version of the JDK is then searched in the list of available versions. If found, Qodana will download and use it. Otherwise, Qodana will download the subsequent version from this list.
You can specify the path to a custom Maven settings file, which lets you use custom repositories, mirrors, credentials, and local repository settings:
This setting lets Qodana parse the path, resolve relative paths against the project root, expose it in the YAML schema, and apply the configured settings.xml file prior to Maven project import or reimport. It also ensures the correct workflow order: Maven reimport (if requested) runs only after Maven settings are configured, which then updates module dependencies.
Override Gradle settings
For JVM linters, you can override the default Gradle settings:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: -v gradle.properties:/data/cache/gradle/gradle.properties
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Once the Qodana run is complete, you can view log files in the $(pwd)/.qodana/results/ directory.
After running Qodana, in the project root run the $ qodana show -d command for opening the directory containing log files.
There are several options for examining Qodana behavior using the /data/results directory:
The /data/results/projectStructure directory.
The Modules.json file in this directory contains a list of all modules detected by Qodana. It should be identical to the list that you expect to see while opening your project in IntelliJ IDEA. If this is no longer the case, check pom.xml for Maven or the build.gradle file for Gradle configurations.
The SDKs.json file in this directory contains the interpreter paths in case of Python.
In the /data/results/ directory, each inspection that detected a possible problem creates its own file named ID.json, where ID is the inspection name that can be used in qodana.yaml for including or excluding inspections. You can find the complete list of inspection IDs in the /data/results/.descriptions.json file using the /groups/*/inspections/*/shortName pattern.
In /data/results/log/idea.log, you can investigate suspicious warnings.
Analysis directories
Using these CLI options, you can override the paths described in the Linter paths section.
Option
Description
Default setting
--repository-root <string>
Specify the VCS root directory for your project. This option is required for Git-related operations
None
-i, --project-dir
Root directory of the inspected project can be either a subdirectory of --repository-root or identical to it.
Files and directories contained in the outside directory are not used while running Qodana
/data/project
-o, --results-dir
Directory to save Qodana inspection results to
/data/results
-r, --report-dir
Directory for saving the generated HTML report. To open the report, you will need to add the --save-report option
Directory inside --project-dir. If missing, the whole project is inspected.
Files and directories contained in the outside directory like .git and build.gradle are used by Qodana while inspecting code
None
--only-directory <string>
Specify the directory inside the project-dir directory that must be analyzed. If not specified, the whole project will be analyzed
Files and directories contained in the outside directory like .git and build.gradle are used by Qodana while inspecting code
None
Override the report directory
This Docker command overrides the default report directory using the --report-dir option, and saves the generated report to the local filesystem using the --save-report option:
The generated report is saved to the local filesystem as configured by the -v <html-report-directory>:/data/results/newreportdir/ line in this command.
Analyze a specific project directory within a repository
A typical project structure can have a directory structure similar to this:
repo/
.git/
project/
...
Here, the repo/.git directory contains information that should be accessible to Qodana, and the repo/project directory contains the project that needs to be inspected by Qodana. All these samples mount the repo/project directory using the --project-dir option, while the QODANA_TOKEN variable refers to the Qodana Cloud project token:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
- 'releases/*'
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --project-dir project
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Exclude paths from the analysis scope
You can exclude files and paths from analyses on a per-analysis basis and for all inspections at once. Information about inspection IDs is available on the Inspectopedia website.
To exclude all paths in a project from the analysis scope, omit the paths node.
Exclude all inspections for specified project paths using the following YAML configuration:
You can improve Qodana performance by persisting cache between analyses. For example, package and dependency management tools such as Maven, Gradle, npm, Yarn, and NuGet keep a local cache of downloaded dependencies.
By default, Qodana save caches to the /data/cache directory inside a container. You can override this location using the --cache-dir option. This data is per-repository, so you can pass cache from branch-a to build checking branch-b. In this case, only new dependencies would be downloaded if they were added.
In a GitHub workflow, you can use dependency caching. GitLab CI/CD also has the cache that can be stored only inside the project directory. In this case, you can exclude the cache directory from inspection via qodana.yaml.
This command maps the local directory with the /data/cache directory of the Docker image, which saves cache to your local filesystem:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --cache-dir /data/newcachedir
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
In this configuration, exceeding just one setting limitation will make the build fail.
The severityThresholds:any key lets you configure the total number of problems. The severityThresholds:critical key lets you configure quality gates for each problem severity. The testCoverageThresholds:fresh and testCoverageThresholds:total keys let you configure the total and fresh code coverage supported by several linters. The dependencyLicenses key lets you configure quality gates for prohibited or unknown licenses.
Also, you can configure quality gates for the total number of problems using the --fail-threshold CLI option. Here is the command that tells Qodana to fail the build in case the number of problems exceeds 10:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --fail-threshold 10
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
In the runner configuration, find the Additional Qodana arguments field and specify a quality gate:
--fail-threshold 10
If you run Qodana with the baseline mode enabled, a threshold is calculated as the sum of new and absent problems. The unchanged results are ignored.
Baseline
In the baseline run mode, each new Qodana run is compared to some initial run. This can help in situations when you have no possibility to fix old problems and rather want to prevent the appearance of new ones.
To use the baseline feature, first run Qodana, and in the report UI select the problems that will be considered as baseline. Finally, save the SARIF-formatted file containing the baseline problems.
This is the list of baseline-related options:
Option
Description
-b, --baseline
Run Qodana in the baseline mode. Provide the path to an existing SARIF report to be used in the baseline state calculation
--baseline-include-absent
Include in the output report the results from the baseline run that are absent during the current analysis
Here, the <path-to-the-SARIF-file> is the path to a qodana.sarif.json file relative to the project root and taken from a previous Qodana run. If --baseline-include-absent is invoked, the inspection results will include absent problems or the problems detected only in the baseline run but not in the current run.
Based on this run, the SARIF output report will contain the per-problem information on the baseline state.
In the example above, the enry dependency is completely excluded from the analysis. Because any possible license-related problems are dismissed, the dependency won't be included in the report at all. This is useful to quickly hide internal dependencies that do not need to be mentioned in the report.
Allow or prohibit a license
Override the predefined license compatibility matrix:
where name is the dependency name, version is the dependency version, and licenses is the list of redefined dependency licenses.
In the example above, you 'tell' Qodana to detect CDDL-1.1, GPL-2.0-with-classpath-exception, and no other licenses for jaxb-runtime (only 2.3.1). This is useful when a dependency is dual-licensed, and you want to omit some license or when it's not possible to detect the license from the dependency sources correctly.
Custom dependencies
Currently, the license audit with Qodana is possible only for JPS, Maven, Gradle, npm, yarn, and composer projects. To include the dependency that should be mentioned in the report but is impossible to detect from the project sources, use customDependencies to specify it:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --apply-fixes/cleanup
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Using the coverage.reportProblems key, you can configure it in a YAML file. The codeCoverageLocations key lets you override the default code coverage directories, for example:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: -v /my/dir/with/coverage:/data/coverage
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --script default
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --property=idea.log.config.file=info.xml
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --property idea.headless.enable.statistics=false
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
In the runner configuration, find the Additional Qodana arguments field and specify the required property:
--property=idea.headless.enable.statistics=false
Configure plugins
Using the idea.required.plugins.id and idea.suppressed.plugins.id properties, you can specify the plugins required for a specific run, and the list of plugins that will be suppressed:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --property qd.<cpp|rust>.startup.timeout.minutes=10
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
Run incremental analysis on a change set like merge or pull requests
If you just finished work and would like to analyze the changes, you can employ the --diff-start option and specify a hash of the commit that will act as a base for comparison, see the Incremental analysis section for details:
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: --diff-start=<GIT_START_HASH>
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: -e _JAVA_OPTIONS=-Xmx6g
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: -e IDEA_PROPERTIES=/data/project/idea.properties
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
In the runner configuration, find the Additional Docker arguments field and configure the IDEA_PROPERTIES variable:
-e IDEA_PROPERTIES=/data/project/idea.properties
Configure root and non-root users
By default, a Docker container runs under the root user, so Qodana can read project information and write inspection results. Therefore, all files in the results/ directory are owned by the root user after the run.
To overcome this, you can run the container as a regular user:
In this case, the results/ directory on the host should already be created and owned by you. Otherwise, Docker will create it as the root user, and Qodana will not be able to write to it.
TeamCity and Qodana CLI run Qodana using a current non-root user. This can be inconvenient if you wish to install dependencies using the apt tool invoked in the bootstrap section.
To run Qodana as a root user in TeamCity, add the -u root option in the Additional Docker arguments field of the Qodana runner configuration.
To run Qodana CLI as a root user, you can append -u root option to the qodana scan command:
qodana scan -u root
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- main # The 'main' branch
- master # The 'master' branch
- 'releases/*' # The release branches
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2026.2
with:
args: -u root
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
To analyze repositories that use Git submodules accessed via SSH, you must authenticate Git operations within the Qodana Docker container. In this case, you need to configure an SSH agent and pass an SSH key with access to the submodule into the container as shown in the snippets below:
Disable strict host key checking for SSH operations
-e QODANA_SKIP_SUBMODULE_UPDATE=true
Skip Git submodule checkout, can be useful if the submodule checkout fails
Cache in Qodana CLI
Qodana CLI stores files in the <userCacheDir> directory, which is mentioned several times throughout this section. Here is the list of <userCacheDir> directory locations depending on the operating system:
Operating System
Path
macOS
~/Library/Caches/
Linux
~/.cache/
Windows
%LOCALAPPDATA%\
If you run the qodana init command in the project directory, Qodana CLI will let you choose the linter that will be run during inspection, and save the choice in qodana.yaml. Once done, you do not need to specify the linter in the commands, which is shown throughout this section.
The detailed description of the qodana init command is available in the Configure projects section.
Manage plugins
You can specify the plugins that will be downloaded and invoked during inspection using the following YAML configuration:
Here, <plugin-id> denotes the Plugin ID from JetBrains Marketplace. For example, for Grazie Professional, the Plugin ID will be com.intellij.grazie.pro. To find the Plugin ID, on the plugin page click the Overview tab and then navigate to the Additional Information section.
Plugin cache is stored in the /data/cache/plugins directory.
To install third-party software required for your plugins, you can:
Develop your custom Dockerfile that starts with FROM jetbrains/qodana.... You can use Qodana Dockerfile examples available on GitHub.
Incorrect Formatting inspection
The IncorrectFormatting inspection consolidates multiple formatting errors contained in a file into a single problem instead of listing every issue separately. Now, a single problem per file is displayed with example snippets to help you fix issues faster.
This feature is available for all linters except Qodana for C/C++, Qodana Community for C/C++, and Qodana Community for .NET.
By default, Qodana recursively collects projects from subdirectories and imports them for analysis. This change enables incremental analysis and fixes for projects where the analyzed project and VCS root are different.
SomeInspectionId inspection is explicitly enabled for all paths, although it is disabled in the profile
AnotherInspectionId inspection is disabled for relative/path and another/relative/path
no inspections are conducted over these paths: asm-test/src/main/java/org, benchmarks, tools
The following example combines multiple settings, including a quality gate, inspection profile customization, and path exclusions, which are common in .NET project setups:
version: "1.0"
# Run the Qodana for .NET linter in native mode
linter: qodana-dotnet
withinDocker: false
# Set a quality gate: fail if the number of problems exceeds 10
failThreshold: 10
# Use the qodana.recommended profile
profile:
name: qodana.recommended
# Exclude specific paths from the analysis
inspections:
- group: ALL
ignore:
- "tests/"
- "bin/"
- "obj/"
# Include an inspection not contained in the qodana.recommended profile
- inspection: SomeSpecificInspectionId
enabled: true
# Restore .NET dependencies
bootstrap: |+
dotnet restore
Use this example while configuring the Qodana for JVM linter:
version: "1.0"
# Run the Qodana for JVM linter
linter: qodana-jvm
# Set the JDK version
jdk:
version: "17"
# Include Java projects for analysis
rootJavaProjects:
- "./gradle-project"
- "./maven-module/pom.xml"
# Quality gate settings
failureConditions:
severityThresholds:
any: 50 # Fail if total number of problems exceeds 50
critical: 1 # Fail if there is at least 1 critical problem
high: 2 # Fail if there are more than 2 high-severity problems
testCoverageThresholds:
fresh: 80 # Fail if fresh code coverage is below 80%
total: 90 # Fail if total code coverage is below 90%
# Disable specific inspections
profile:
inspections:
- inspection: CheckDependencyLicenses
enabled: false # Disable license audit if not needed
# Include custom plugins
plugins:
- id: com.intellij.grazie.pro