Skip to content

Commit 7103836

Browse files
committed
feat(analyze): add SARIF, GitHub and GitLab output formats
`robotcode analyze code` gains a local `--output-format` flag (overrides the global `--format` for this command) with these values: - `concise` (default): the human-readable text output - `json` / `json-indent`: structured result (diagnostics + summary) - `sarif`: SARIF 2.1.0 log for GitHub code scanning and other consumers - `github`: GitHub Actions workflow annotations (`::error file=...`) - `gitlab`: GitLab Code Quality report (JSON array) `--output-file FILE` writes the report to a file instead of stdout (for the json/json-indent/sarif/github/gitlab formats), which is handy for CI artifact upload. A missing target directory raises a clear UsageError and other I/O errors surface as a ClickException instead of a traceback. Mapping details shared across the machine formats: - LSP 0-based ranges become the 1-based positions SARIF/GitHub expect - severities collapse onto each format's levels (e.g. SARIF error/warning/ note, GitHub error/warning/notice, GitLab major/minor/info) - artifact paths are POSIX and relative to the project root (what the CI integrations expect); `--full-paths` switches to absolute paths - SARIF and GitLab results carry a stable fingerprint (independent of line number) so alerts survive unrelated edits - GitHub property/message values are escaped per the @actions/toolkit rules
1 parent fc76cf3 commit 7103836

4 files changed

Lines changed: 776 additions & 15 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
"""Minimal SARIF 2.1.0 object model for `robotcode analyze code`.
2+
3+
Only the subset of SARIF that we actually emit is modelled. All classes inherit
4+
from `CamelSnakeMixin`, which turns snake_case fields into the camelCase keys
5+
SARIF expects (`ruleId`, `physicalLocation`, `startLine`, …). Fields that are
6+
not valid Python identifiers (`$schema`) use a `field(metadata={"alias": ...})`.
7+
8+
Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
9+
"""
10+
11+
from dataclasses import dataclass, field
12+
from typing import Dict, List, Optional
13+
14+
from robotcode.core.utils.dataclasses import CamelSnakeMixin
15+
16+
SARIF_VERSION = "2.1.0"
17+
SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json"
18+
19+
20+
@dataclass
21+
class Message(CamelSnakeMixin):
22+
text: str
23+
24+
25+
@dataclass
26+
class ArtifactLocation(CamelSnakeMixin):
27+
uri: str
28+
uri_base_id: Optional[str] = None
29+
30+
31+
@dataclass
32+
class Region(CamelSnakeMixin):
33+
# SARIF regions are 1-based for both line and column.
34+
start_line: int
35+
start_column: Optional[int] = None
36+
end_line: Optional[int] = None
37+
end_column: Optional[int] = None
38+
39+
40+
@dataclass
41+
class PhysicalLocation(CamelSnakeMixin):
42+
artifact_location: ArtifactLocation
43+
region: Optional[Region] = None
44+
45+
46+
@dataclass
47+
class Location(CamelSnakeMixin):
48+
physical_location: Optional[PhysicalLocation] = None
49+
message: Optional[Message] = None
50+
51+
52+
@dataclass
53+
class ReportingDescriptor(CamelSnakeMixin):
54+
"""A rule definition (tool.driver.rules[])."""
55+
56+
id: str
57+
name: Optional[str] = None
58+
59+
60+
@dataclass
61+
class Result(CamelSnakeMixin):
62+
rule_id: str
63+
message: Message
64+
level: str = "warning" # error | warning | note | none
65+
rule_index: Optional[int] = None
66+
locations: Optional[List[Location]] = None
67+
related_locations: Optional[List[Location]] = None
68+
partial_fingerprints: Optional[Dict[str, str]] = None
69+
70+
71+
@dataclass
72+
class ToolComponent(CamelSnakeMixin):
73+
name: str
74+
version: Optional[str] = None
75+
information_uri: Optional[str] = None
76+
rules: Optional[List[ReportingDescriptor]] = None
77+
78+
79+
@dataclass
80+
class Tool(CamelSnakeMixin):
81+
driver: ToolComponent
82+
83+
84+
@dataclass
85+
class Run(CamelSnakeMixin):
86+
tool: Tool
87+
results: List[Result] = field(default_factory=list)
88+
89+
90+
@dataclass
91+
class SarifLog(CamelSnakeMixin):
92+
runs: List[Run] = field(default_factory=list)
93+
version: str = SARIF_VERSION
94+
schema: str = field(default=SARIF_SCHEMA, metadata={"alias": "$schema"})

0 commit comments

Comments
 (0)