Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
95740d0
add thift api changes, add vue component, update api version
feyruzb Jan 13, 2026
8ca0a56
add migration script
feyruzb Jan 13, 2026
d304eca
correct save button location
feyruzb Jan 13, 2026
251a336
add new loading systems for presets
feyruzb Feb 6, 2026
424d306
add cli flow for filter preset add/list/delete/load
feyruzb Feb 17, 2026
92c6635
add functional tests for thrift functions
feyruzb Feb 17, 2026
5a3a5ca
linter fixing phase
feyruzb Feb 18, 2026
3ef93d6
change database querying, now 2 times less requests, fix bug of not l…
feyruzb Feb 25, 2026
afc62e0
rebase
feyruzb Feb 26, 2026
5f2e79e
fix wrong naming of --filter-preset and remove filter merging
feyruzb Feb 26, 2026
e2f8db5
fix wrong naming of preset-filter for cli
feyruzb Feb 26, 2026
0132c0c
add change on preset detection, fix diff-type wrong load content
feyruzb Feb 27, 2026
1400422
add overriding and saving as new extension to saving presets
feyruzb Mar 2, 2026
003fad6
modify behavior of function according to new agreed one
feyruzb Mar 2, 2026
462d44e
Load saved, overridden, or newly created preset after successful oper…
feyruzb Mar 2, 2026
53cd471
remove extra wait, remove --force cli option
feyruzb Mar 2, 2026
cbf1265
fix majority of comments
feyruzb Mar 2, 2026
2f98c8d
fix wrong date build, move default values to avoid circular import
feyruzb Mar 4, 2026
cda9d3e
fix functional tests, fix cli date loading
feyruzb Mar 6, 2026
799513f
fix no meaning field loading bug, fix bug path length dead code bug
feyruzb Mar 6, 2026
bc6698d
add new funtionality and fix comments
feyruzb Mar 9, 2026
3149eda
fix remaining comments
feyruzb Mar 11, 2026
a530f6f
add cli tests
feyruzb Mar 11, 2026
bf8606b
add missing fullReportPathInComponent in build for GUI
feyruzb Mar 11, 2026
0d4f536
fix incorrect naming for cleanup-plan
feyruzb Mar 11, 2026
1b36295
fix storing filter bug
feyruzb Mar 12, 2026
dda5933
move --filter-preset to correct subcomand space
feyruzb Mar 12, 2026
c79e691
add test for applying filter presets and fix other tests with new int…
feyruzb Mar 16, 2026
08900d2
fix linter
feyruzb Mar 16, 2026
27291c9
fix preset filter tests interfering with cmdline tests
feyruzb Mar 16, 2026
288e2e7
add documentation for cli functionality
feyruzb Mar 16, 2026
6ff79f7
fix pylint f string bug for optimization
feyruzb Mar 16, 2026
e29092d
replace localhost url with general <url>
feyruzb Mar 17, 2026
74e81fe
fix tests
feyruzb Mar 17, 2026
009748f
psql failing fix
feyruzb Mar 18, 2026
0edf927
remove thrift rebuild script, will be introduced in another pr
feyruzb Mar 18, 2026
c3e5fb9
fixed comments
feyruzb Mar 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions codechecker_common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,23 @@ def generate_random_token(num_bytes: int = 32) -> str:
return hash_value[idx:(idx + num_bytes)]


def thrift_to_json(obj):
"""
Recursively convert a Thrift object (or any object with __dict__)
into a JSON-serializable dict, skipping None-valued fields.
"""
if obj is None or isinstance(obj, (str, int, float, bool)):
return obj
if isinstance(obj, list):
return [thrift_to_json(x) for x in obj]
if isinstance(obj, dict):
return {k: thrift_to_json(v) for k, v in obj.items()}
if hasattr(obj, '__dict__'):
return {k: thrift_to_json(v) for k, v in obj.__dict__.items()
if v is not None}
return str(obj)


def format_size(num: float, suffix: str = 'B') -> str:
"""
Pretty print storage units.
Expand Down
98 changes: 98 additions & 0 deletions docs/web/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
- [List runs (`runs`)](#list-runs-runs)
- [List of run histories (`history`)](#list-of-run-histories-history)
- [List analysis results' summary (`results`)](#list-analysis-results-summary-results)
- [Manage filter presets (`filter-preset`)](#manage-filter-presets-filter-preset)
- [Creating a filter preset](#creating-a-filter-preset)
- [Listing filter presets](#listing-filter-presets)
- [Deleting a filter preset](#deleting-a-filter-preset)
- [Applying a filter preset to results](#applying-a-filter-preset-to-results)
- [Example](#example-1)
- [Show differences between two runs (`diff`)](#show-differences-between-two-runs-diff)
- [Show summarised count of results (`sum`)](#show-summarised-count-of-results-sum)
Expand Down Expand Up @@ -1122,7 +1127,100 @@ CodeChecker cmd results my_run --severity critical high medium \

# Get detailed analysis results for a run in JSON format.
CodeChecker cmd results -o json --details my_run

# Get analysis results using a saved filter preset:
CodeChecker cmd results my_run --filter-preset my_preset \
--url <PRODUCT_URL>
```

#### Manage filter presets (`filter-preset`)

Filter presets allow you to save a named set of filter parameters on the
server so they can be reused across multiple queries without having to
specify all the filter flags every time.

A preset stores any combination of the filter arguments available to
`cmd results` (severity, checker name, file path, detection status, etc.).

##### Creating a filter preset

```
# Create a minimal preset that filters by severity:
CodeChecker cmd filter-preset new \
--name "high_and_critical" \
--url <PRODUCT_URL> \
--severity critical high

# Create a preset with many filter parameters:
CodeChecker cmd filter-preset new \
--name "FullPreset" \
--url <PRODUCT_URL> \
--severity critical high medium low style unspecified \
--review-status unreviewed confirmed false_positive intentional \
--detection-status new reopened unresolved resolved off unavailable \
--report-status outstanding closed \
--file "*/src/*.cpp" "*/include/*.h" \
--checker-name "deadcode.DeadStores" "core.NullDereference" \
--checker-msg "*null pointer*" "*memory leak*" \
--analyzer-name clangsa clang-tidy \
--component myComponent \
--tag v1.0 v2.0 \
--report-hash "abcdef1234567890" \
--bug-path-length "1:50" \
--detected-before "2026:03:09:00:00:00" \
--detected-after "2025:01:01:00:00:00" \
--fixed-before "2026:03:09:00:00:00" \
--fixed-after "2025:06:01:00:00:00" \
--outstanding-reports-date "2026:03:01:00:00:00" \
--anywhere-on-report-path \
--single-origin-report \
--uniqueing off
```

Preset names must be unique. Attempting to create a preset with an
already-existing name will fail.

##### Listing filter presets

```
# List all presets (default table output):
CodeChecker cmd filter-preset list \
--url <PRODUCT_URL>

# List presets in CSV format:
CodeChecker cmd filter-preset list \
--url <PRODUCT_URL> \
--output csv

# List presets in JSON format:
CodeChecker cmd filter-preset list \
--url <PRODUCT_URL> \
--output json
```

##### Deleting a filter preset

You can find the preset ID by [listing filter presets](#listing-filter-presets).

```
# Delete a preset by its ID (shown in the list output):
CodeChecker cmd filter-preset delete \
--id 1 \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a sentence about how to get an ID, for example with a link to the previous section.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

--url <PRODUCT_URL>
```

##### Applying a filter preset to results

Use the `--filter-preset` flag with `cmd results` to apply a saved preset:

```
CodeChecker cmd results my_run \
--filter-preset "high_and_critical" \
--url <PRODUCT_URL>
```

**Note:** `--filter-preset` cannot be combined with other filter arguments.
Either use a preset or specify filters on the command line, not both.

#### Show differences between two runs (`diff`)

Expand Down
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/js/codechecker-api-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codechecker-api",
"version": "6.67.0",
"version": "6.68.0",
"description": "Generated node.js compatible API stubs for CodeChecker server.",
"main": "lib",
"homepage": "https://github.com/Ericsson/codechecker",
Expand Down
Binary file modified web/api/py/codechecker_api/dist/codechecker_api.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/py/codechecker_api/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with open('README.md', encoding='utf-8', errors="ignore") as f:
long_description = f.read()

api_version = '6.67.0'
api_version = '6.68.0'

setup(
name='codechecker_api',
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion web/api/py/codechecker_api_shared/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
with open('README.md', encoding='utf-8', errors="ignore") as f:
long_description = f.read()

api_version = '6.67.0'
api_version = '6.68.0'

setup(
name='codechecker_api_shared',
Expand Down
40 changes: 40 additions & 0 deletions web/api/report_server.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,12 @@ struct ReportFilter {
24: optional bool fullReportPathInComponent,
}

struct FilterPreset {
1: i64 id, // Unique ID of "FilterPreset".
2: string name, // Human readable name of preset.
3: ReportFilter reportFilter // Uniquely configured ReportFilter.
}

struct RunReportCount {
1: i64 runId, // Unique ID of the run.
2: string name, // Human readable name of the run.
Expand Down Expand Up @@ -583,6 +589,40 @@ service codeCheckerDBAccess {
4: optional RunSortMode sortMode)
throws (1: codechecker_api_shared.RequestFailed requestError),

//============================================
// Filter grouping api calls.
//============================================

// Stores the given FilterPreset with the given id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got a few questions / remarks regarding this part:

  • Does it check the id of the incoming FilterPreset to decide if it exists or not? If so, please modify the first line accordingly (e.g. "If the a preset exists with the given id, it overwrites the name, and all preset values.")
  • Fix the second line to match the first: "If the preset does not exist yet, it creates it with the given id.".
  • The line "if the id is -1 a new preset filter is created" is confusing so if I add -1 as an id, it forcibly creates the filter? And if so, what will be the actual id after creation?
  • Please make all new sentences start with a capital letter and finish with a dot.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// If the preset exists with the given id, it overwrites the name, and all preset values
// If the preset does not exist yet, throws and error
// If the id is -1 a new preset filter is created and the id of the new preset is returned.
// If a preset with that name already existed, it throws an error. Thus the "FilterPreset" name must be unique.
// The encoding of the name must be unicode. (whitespaces allowed)
// Maximum "FilterPreset" name 50
// Returns: the id of the modified or created preset
// PERMISSION: PRODUCT_ADMIN
i64 storeFilterPreset(1: FilterPreset preset)
throws (1: codechecker_api_shared.RequestFailed requestError);

// Returns the "FilterPreset" identified by id
// Throws and error in case there is no preset with the given id
// PERMISSION: PRODUCT_VIEW
FilterPreset getFilterPreset(1: i64 id)
throws (1: codechecker_api_shared.RequestFailed requestError);

// Removes the FilterPreset with the given id
// Returns the id of the "FilterPreset" removed
// Throws an error if the preset with the given id does not exist.
// PERMISSION: PRODUCT_ADMIN
i64 deleteFilterPreset(1: i64 id)
throws (1: codechecker_api_shared.RequestFailed requestError);

// Returns all "FilterPreset"s stored for the product repository
// PERMISSION: PRODUCT_VIEW
list <FilterPreset> listFilterPreset()
throws (1: codechecker_api_shared.RequestFailed requestError);

// Returns the number of available runs based on the run filter parameter.
// PERMISSION: PRODUCT_VIEW
i64 getRunCount(1: RunFilter runFilter)
Expand Down
109 changes: 101 additions & 8 deletions web/client/codechecker_client/cli/cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,13 @@
product_client, \
source_component_client, \
task_client, \
token_client
token_client, \
filter_preset_client
from codechecker_client.filter_defaults import DEFAULT_FILTER_VALUES

from codechecker_common import arg, logger, util
from codechecker_common.output import USER_FORMATS

DEFAULT_FILTER_VALUES = {
'review_status': ['unreviewed', 'confirmed'],
'detection_status': ['new', 'reopened', 'unresolved'],
'uniqueing': 'off',
'anywhere_on_report_path': False,
'single_origin_report': False
}

DEFAULT_OUTPUT_FORMATS = ["plaintext"] + USER_FORMATS

Expand Down Expand Up @@ -448,6 +443,32 @@ def init_default(dest):
"entirely in the files specified by the "
"given --component.")

f_group.add_argument('--report-status',
nargs='*',
dest="report_status",
metavar='REPORT_STATUS',
default=init_default('report_status'),
help="R|Filter results by report statuses.\n"
"Reports can be assigned a report status of the "
"following values:\n"
"- Outstanding: Currently detected and still unresolved reports.\n"
"- Closed: Reports marked as fixed, false positive, or otherwise resolved.\n" +
warn_diff_mode)


def __add_filter_preset_argument(parser):
"""Add the --filter-preset argument to the given parser."""
parser.add_argument('--filter-preset',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option shouldn't be available for CodeChecker cmd filter-preset new, because it's confusing to see both --name and --filter-preset at the same time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, now this option only appears for results, diff, sum.

type=str,
dest='filter_preset_name',
metavar='PRESET_NAME',
required=False,
default=argparse.SUPPRESS,
help="Use a pre-configured filter preset. The preset "
"is loaded from the server and applied to the "
"results. Use 'CodeChecker cmd filter-preset "
"list' to see available presets.")


def __register_results(parser):
"""
Expand Down Expand Up @@ -478,6 +499,7 @@ def __register_results(parser):
"events, bug report points etc.")

__add_filtering_arguments(parser, DEFAULT_FILTER_VALUES)
__add_filter_preset_argument(parser)


def __register_diff(parser):
Expand Down Expand Up @@ -543,6 +565,7 @@ def __register_diff(parser):
"the reported defect.")

__add_filtering_arguments(parser, DEFAULT_FILTER_VALUES, True)
__add_filter_preset_argument(parser)

group = parser.add_argument_group(
"comparison modes",
Expand Down Expand Up @@ -623,6 +646,7 @@ def __register_sum(parser):
default_filter_values = DEFAULT_FILTER_VALUES
default_filter_values['uniqueing'] = 'on'
__add_filtering_arguments(parser, default_filter_values)
__add_filter_preset_argument(parser)


def __register_delete(parser):
Expand Down Expand Up @@ -1530,6 +1554,62 @@ def __register_del(parser):
__add_common_arguments(del_t, needs_product_url=False)


def __register_filter_presets(parser):
"""
Add argparse subcommand parser for the "filter preset management" action.
"""

def __register_new(parser):
parser.add_argument('--name',
type=str,
dest='preset_name',
required=True,
metavar='PRESET_NAME',
help="Name of the filter preset to create.")
__add_filtering_arguments(parser)

def __register_delete(parser):
"""
Add argparse subcommand parser for the "delete preset" action.
"""
parser.add_argument('--id',
type=int,
dest='preset_id',
required=True,
metavar='PRESET_ID',
help="ID of the filter preset to delete.")

subcommands = parser.add_subparsers(title='available actions')

# Create handlers for individual subcommands.
list_presets = subcommands.add_parser(
'list',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="List all filter presets available on the server.",
help="List all filter presets.")
list_presets.set_defaults(func=filter_preset_client.handle_list_presets)
__add_common_arguments(list_presets,
output_formats=DEFAULT_OUTPUT_FORMATS)

new_preset = subcommands.add_parser(
'new',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be set instead of new because it not only creates but updates presets, too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, now only creates new.

formatter_class=arg.RawDescriptionDefaultHelpFormatter,
description="Create a new filter preset.",
help="Create a new filter preset.")
__register_new(new_preset)
new_preset.set_defaults(func=filter_preset_client.handle_new_preset)
__add_common_arguments(new_preset)

delete_preset = subcommands.add_parser(
'delete',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Delete a filter preset from the server.",
help="Delete a filter preset.")
__register_delete(delete_preset)
delete_preset.set_defaults(func=filter_preset_client.handle_delete_preset)
__add_common_arguments(delete_preset)


def add_arguments_to_parser(parser):
"""
Add the subcommand's arguments to the given argparse.ArgumentParser.
Expand Down Expand Up @@ -1820,5 +1900,18 @@ def add_arguments_to_parser(parser):
tasks.set_defaults(func=task_client.handle_tasks)
__add_common_arguments(tasks, needs_product_url=False)

filter_preset = subcommands.add_parser(
'filter-preset',
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description="Manage filter presets of a CodeChecker server. "
"Filter presets are named collections of filter "
"configurations that can be applied to the analysis "
"results of a run. Please see the individual "
"subcommands for details.",
help="Access subcommands related to configuring filter presets of a "
"CodeChecker server.")
__register_filter_presets(filter_preset)
__add_common_arguments(filter_preset)

# 'cmd' does not have a main() method in itself, as individual subcommands are
# handled later on separately.
Loading
Loading