Run Qodana in GitHub Actions: From Workflow to Code Scanning
Learn how to integrate Qodana into GitHub Actions, output SARIF, and view results in Code Scanning. Includes a minimal workflow, a sample .qodana.yml, and tips for avoiding common pitfalls.
19 Dec 2025, 14:50 UTC

Problem & Takeaway
Teams want automated static‑analysis feedback that integrates directly into GitHub’s Code Scanning interface. Qodana, JetBrains’ static‑analysis engine, can be run inside GitHub Actions and emit SARIF (Static Analysis Results Interoperability Format) that GitHub consumes. The key takeaway: a single workflow file plus a lightweight .qodana.yml is enough to get Qodana results visible in the repository’s Security tab, provided you respect the free tier limits and supply a license key when needed.
Setting Up the Workflow
Place the following YAML in .github/workflows/qodana.yml. It triggers on pushes to main, checks out the repository, runs Qodana, and uploads the SARIF artifact.
name: Qodana Static Analysis
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # required for accurate line numbers
- name: Run Qodana
uses: jetbrains/qodana-action@v2023.3
with:
qodana_version: 2023.3.1
output: sarif
# Optional: specify a custom output directory
# output_directory: qodana-results
env:
QODANA_LICENSE_KEY: ${{ secrets.QODANA_LICENSE_KEY }}
- name: Upload SARIF
if: always()
uses: actions/upload-artifact@v4
with:
name: qodana-sarif
path: qodana.sarif
Key points:
fetch-depth: 0ensures the action can compute accurate line numbers for findings.- The
qodana_versionmust match the language version you target; newer Qodana releases add support for newer language features. - Pass
QODANA_LICENSE_KEYas a repository secret only if you want to enable paid rule sets. The free tier works without it. - Set
output: sarifso the action writesqodana.sarifin the workflow’s working directory.
Configuring Qodana with .qodana.yml
Qodana automatically loads a .qodana.yml from the repository root. A minimal file that turns on the Java/Kotlin rule set and sets a custom output directory looks like this:
version: 1
include:
- name: java
rules: all
output:
directory: qodana-results
Supported keys:
include– list of language modules to enable. Each module can specifyrules: allor a comma‑separated list of rule IDs.output.directory– where Qodana writes its SARIF and HTML reports. GitHub Actions will still pick upqodana.sariffrom the root unless you change theoutputparameter in the action.
Running the Action
When the workflow runs:
- The action pulls the Qodana Docker image (default
jetbrains/qodana:2023.3.1). - It executes
qodana runinside the container, scanning the repository according to the.qodana.yml. - Results are emitted as
qodana.sarifand optionally as an HTML report. - GitHub interprets the SARIF file and displays findings in the Security > Code Scanning tab.
Verify the run by navigating to the Actions tab, selecting the latest workflow run, and downloading the qodana-sarif artifact. Open it in a text editor; you should see entries like:
{
"ruleId": "java:S100",
"message": {
"text": "Class name 'foo' must match pattern '^[A-Z][a-zA-Z0-9]*$'"
},
"locations": [
{
"physicalLocation": {
"artifactLocation": {"uri": "src/main/java/com/example/Foo.java"},
"region": {"startLine": 12, "startColumn": 5}
}
}
]
}
Common Pitfalls & How to Avoid Them
- Action name miss‑spelled: The official action is
jetbrains/qodana-action. A typo will cause the job to fail immediately. - Outdated
qodana_version: Using a version that predates your language runtime can produce syntax errors. Keep the version in sync with theqodana.ymllanguage modules. - Missing
QODANA_LICENSE_KEYfor paid rules: If you enable a paid rule set, the action will exit with a 401 error. Store the key inSettings > Secretsand reference it as shown. - Shallow clone:
actions/checkout@v4defaults to a shallow clone (depth 1). Line numbers in SARIF may be wrong. Usefetch-depth: 0to avoid this. - Memory limits: The container has a 4 GB RAM ceiling. Very large projects may hit this limit and abort with a “Memory limit exceeded” error. Split the analysis into smaller modules or use a self‑hosted runner with more memory.
- Runtime limits: The free tier caps the action at 10 minutes. If the analysis takes longer, the job fails. Optimize your
.qodana.ymlby disabling unnecessary modules.
Limits & Workarounds
| Limit | Impact | Mitigation |
|---|---|---|
| 4 GB container memory | Large monorepos may crash | Use self‑hosted runners with higher memory, or split analysis per sub‑module. |
| 10 minute runtime | Analysis aborts for big codebases | Enable only core rule sets, or run Qodana in stages. |
| Free tier rule set | Missing advanced checks | Set QODANA_LICENSE_KEY to unlock paid rules. |
| Shallow clone path resolution | Inaccurate file paths in SARIF | Set fetch-depth: 0 in checkout step. |
Practical Verification Checklist
- Push a commit that introduces a known issue (e.g., a class name that violates a naming rule).
- Confirm the workflow runs and completes.
- Download the SARIF artifact and inspect that the issue appears.
- Open Security > Code Scanning and verify the issue is listed, with a link that opens the offending line in the editor.
- Check the job logs for a line like
Qodana finished. SARIF written to qodana.sarifto confirm successful completion.
Conclusion
Integrating Qodana into GitHub Actions is straightforward once you understand the workflow structure and the SARIF output format. By adding a small .qodana.yml and configuring the action parameters correctly, you can get continuous static‑analysis feedback directly within GitHub’s native Code Scanning UI. Always be mindful of the free tier limits and configure your repository to avoid the common pitfalls highlighted above.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.