diff --git a/.codeclimate.yml b/.codeclimate.yml
index 40730a7962db2bce5ee699ca5dbf07ec704e8ebf..761a90adf2de1c97afed0a92ea8102607de09957 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -1,7 +1,7 @@
 version: "2"
 exclude_patterns:
   - "ThirdParty/"
-  - "Tests/Data/"
+  - "Tests/"
 plugins:
-  markdownlint:
-    enabled: true
+  duplication:
+    enabled: false
diff --git a/scripts/ci/jobs/code-quality.yml b/scripts/ci/jobs/code-quality.yml
index 4a5b63d3a744bb28d4f4e344f56ac64ee5986f3d..281dd53103cfac983174c0388a0287c3f3a2c08e 100644
--- a/scripts/ci/jobs/code-quality.yml
+++ b/scripts/ci/jobs/code-quality.yml
@@ -1,3 +1,19 @@
+cppcheck:
+  stage: check
+  image: $CONTAINER_GCC_IMAGE
+  needs: []
+  before_script:
+    - mkdir -p build
+    - cd build
+  script: >-
+    cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DOGS_USE_CONAN=OFF \
+      -DOGS_USE_UNITY_BUILDS=OFF -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
+    bash cppcheck.sh
+  artifacts:
+    reports:
+      codequality: build/cppcheck.json
+    expire_in: 1 week
+
 code_quality:
   stage: check
   tags: [docker, envinf1]
diff --git a/scripts/cmake/CppCheck.cmake b/scripts/cmake/CppCheck.cmake
index 7fd5e832dfa2edd3ea299703d23f7a74a2432515..a960a96ae0cb8e5882c4aec308b8ba6503070bf1 100644
--- a/scripts/cmake/CppCheck.cmake
+++ b/scripts/cmake/CppCheck.cmake
@@ -1,6 +1,8 @@
 if(NOT CPPCHECK_TOOL_PATH)
     return()
 endif()
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+configure_file(${PROJECT_SOURCE_DIR}/scripts/test/cppcheck.in.sh ${PROJECT_BINARY_DIR}/cppcheck.sh)
 
 if(DEFINED ENV{NUM_THREADS})
     set(CPPCHECK_THREADS -j $ENV{NUM_THREADS})
@@ -8,9 +10,11 @@ endif()
 
 add_custom_target(cppcheck
     COMMAND ${CPPCHECK_TOOL_PATH}
-        --force
+        --project=${PROJECT_BINARY_DIR}/compile_commands.json
+        --language=c++
+        --std=c++17
         --enable=all
-        # --inconclusive
+        --inconclusive
         ${CPPCHECK_THREADS}
         -i ${PROJECT_BINARY_DIR}/CMakeFiles
         -i ${PROJECT_SOURCE_DIR}/ThirdParty
diff --git a/scripts/test/cppcheck.in.sh b/scripts/test/cppcheck.in.sh
new file mode 100644
index 0000000000000000000000000000000000000000..72a922ed7113d0564c7d75e16e313bd2aaf5b2e8
--- /dev/null
+++ b/scripts/test/cppcheck.in.sh
@@ -0,0 +1,28 @@
+# Runs cppcheck with GitLab CI (CodeClimate) output
+OUTPUT_FILE=${PROJECT_BINARY_DIR}/cppcheck.json
+${CPPCHECK_TOOL_PATH} \
+        --project=${PROJECT_BINARY_DIR}/compile_commands.json \
+        --language=c++ \
+        --std=c++17 \
+        --enable=all \
+        --inconclusive \
+        -j 4 \
+        -i ${PROJECT_BINARY_DIR}/CMakeFiles \
+        -i ${PROJECT_SOURCE_DIR}/ThirdParty \
+        -i ${PROJECT_SOURCE_DIR}/Applications/DataExplorer \
+        -i ${PROJECT_SOURCE_DIR}/Tests \
+        --template='{\n  "description": "{message}",\n  "location": {\n    "path": "{file}",\n    "lines": {\n      "begin": {line}\n    }\n  }\n},' \
+        --output-file=$OUTPUT_FILE \
+
+echo "$( \
+  # add brackets
+  printf '[\n'; \
+  cat $OUTPUT_FILE | \
+  # strip source code absolute path
+  sed 's|${PROJECT_SOURCE_DIR}/||' | \
+  # escape strings
+  sed 's/string literal "\(.*\)" to/string literal \\"\1\\" to/g' | \
+  # remove last comma
+  sed '$s/,$//'; \
+  printf ']\n')" \
+  > $OUTPUT_FILE