diff --git a/docs/schemas/codeql-metrics-schema.json b/docs/schemas/codeql-metrics-schema.json new file mode 100644 index 00000000000..1b1039ec2b3 --- /dev/null +++ b/docs/schemas/codeql-metrics-schema.json @@ -0,0 +1,143 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://eclipse-score.github.io/codeql-metrics-schema.json", + "title": "CodeQL Metrics Schema", + "description": "Schema for code quality metrics extracted from CodeQL SARIF results", + "type": "object", + "required": [ + "schema_version", + "generated_by", + "overall_metrics", + "metrics_by_severity", + "metrics_by_rule" + ], + "additionalProperties": false, + "properties": { + "schema_version": { + "type": "string", + "const": "1", + "description": "Schema version for backward compatibility" + }, + "generated_by": { + "type": "string", + "description": "Tool/script that generated this metrics file (e.g., 'extract_codeql_metrics.py')" + }, + "generated_at": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when metrics were generated" + }, + "overall_metrics": { + "type": "object", + "description": "Aggregate metrics across all findings", + "additionalProperties": false, + "required": [ + "total_findings", + "errors", + "warnings", + "notes" + ], + "properties": { + "total_findings": { + "type": "integer", + "minimum": 0, + "description": "Total count of all findings" + }, + "errors": { + "type": "integer", + "minimum": 0, + "description": "Count of findings with severity 'error'" + }, + "warnings": { + "type": "integer", + "minimum": 0, + "description": "Count of findings with severity 'warning'" + }, + "notes": { + "type": "integer", + "minimum": 0, + "description": "Count of findings with severity 'note'" + } + } + }, + "metrics_by_severity": { + "type": "object", + "description": "Detailed breakdown by severity level with percentages", + "additionalProperties": false, + "properties": { + "error": { + "$ref": "#/$defs/severityMetrics" + }, + "warning": { + "$ref": "#/$defs/severityMetrics" + }, + "note": { + "$ref": "#/$defs/severityMetrics" + } + } + }, + "metrics_by_rule": { + "type": "object", + "description": "Findings grouped by rule ID, sorted by count (descending). Keys are rule IDs.", + "additionalProperties": { + "$ref": "#/$defs/ruleMetrics" + } + } + }, + "$defs": { + "severityMetrics": { + "type": "object", + "additionalProperties": false, + "required": [ + "count", + "percentage" + ], + "properties": { + "count": { + "type": "integer", + "minimum": 0, + "description": "Number of findings at this severity level" + }, + "percentage": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "Percentage of total findings" + } + } + }, + "ruleMetrics": { + "type": "object", + "additionalProperties": false, + "required": [ + "count", + "severity" + ], + "properties": { + "count": { + "type": "integer", + "minimum": 1, + "description": "Number of findings for this rule" + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "note" + ], + "description": "Severity level of findings for this rule" + }, + "rule_name": { + "type": "string", + "description": "Human-readable rule name/description" + }, + "rule_url": { + "type": "string", + "format": "uri", + "description": "URL to rule documentation (if available)" + } + } + } + } +} diff --git a/scripts/tooling/BUILD b/scripts/tooling/BUILD index c2088414897..7a8454ff8a4 100644 --- a/scripts/tooling/BUILD +++ b/scripts/tooling/BUILD @@ -81,6 +81,22 @@ py_binary( visibility = ["//visibility:public"], ) +py_binary( + name = "extract_codeql_metrics", + srcs = ["cli/workflow/extract_codeql_metrics.py"], + main = "cli/workflow/extract_codeql_metrics.py", + visibility = ["//visibility:public"], + deps = [":lib"] + all_requirements, +) + +py_binary( + name = "generate_misra_report", + srcs = ["cli/workflow/generate_misra_report.py"], + main = "cli/workflow/generate_misra_report.py", + visibility = ["//visibility:public"], + deps = [":lib"] + all_requirements, +) + # Tests target score_py_pytest( name = "tooling_tests", diff --git a/scripts/tooling/cli/main.py b/scripts/tooling/cli/main.py index 36b138ad729..b3539a94e38 100644 --- a/scripts/tooling/cli/main.py +++ b/scripts/tooling/cli/main.py @@ -21,9 +21,11 @@ def main() -> None: from scripts.tooling.cli.misc import register as _register_misc from scripts.tooling.cli.release import register as _register_release + from scripts.tooling.cli.workflow import register as _register_workflow _register_misc(subparsers) _register_release(subparsers) + _register_workflow(subparsers) args = parser.parse_args() sys.exit(args.func(args)) diff --git a/scripts/tooling/cli/misc/assets/report_template.html b/scripts/tooling/cli/misc/assets/report_template.html index f3b501af52c..ae72d3c7db7 100644 --- a/scripts/tooling/cli/misc/assets/report_template.html +++ b/scripts/tooling/cli/misc/assets/report_template.html @@ -247,6 +247,124 @@ } .pat-notice a { color: var(--accent); } + /* ── Code Quality Section ──────────────────────────────────── */ + #code-quality-section { + max-width: 1400px; + margin: 2.5rem auto; + padding: 1.5rem; + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + display: none; + } + #code-quality-section.visible { display: block; } + + .cq-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 1.5rem; + } + .cq-header h2 { + font-size: 1.1rem; + font-weight: 600; + color: #e6edf3; + margin: 0; + } + .cq-header .cq-timestamp { + font-size: 0.75rem; + color: var(--muted); + } + + .cq-metrics-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; + margin-bottom: 1.5rem; + } + + .cq-metric-card { + background: #0d1117; + border: 1px solid var(--border); + border-radius: 6px; + padding: 1rem; + text-align: center; + } + .cq-metric-card .cq-value { + font-size: 2rem; + font-weight: 700; + margin-bottom: 0.3rem; + } + .cq-metric-card.error .cq-value { color: var(--red); } + .cq-metric-card.warning .cq-value { color: var(--yellow); } + .cq-metric-card.note .cq-value { color: var(--muted); } + .cq-metric-card .cq-label { + font-size: 0.82rem; + color: var(--muted); + } + .cq-metric-card .cq-percent { + font-size: 0.75rem; + color: var(--muted); + margin-top: 0.4rem; + } + + .cq-rules-section { + margin-top: 1.5rem; + } + .cq-rules-header { + font-size: 0.95rem; + font-weight: 600; + color: #e6edf3; + margin-bottom: 0.8rem; + } + .cq-rules-table { + width: 100%; + border-collapse: collapse; + font-size: 0.82rem; + } + .cq-rules-table th { + background: #161b22; + border: 1px solid var(--border); + padding: 0.6rem 0.8rem; + text-align: left; + font-weight: 600; + color: var(--text); + } + .cq-rules-table td { + border: 1px solid var(--border); + padding: 0.6rem 0.8rem; + color: var(--text); + } + .cq-rules-table tr:hover { background: #161b22; } + .cq-rule-name { font-weight: 500; } + .cq-rule-count { + text-align: center; + font-weight: 600; + } + .cq-rule-severity { + text-align: center; + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; + padding: 0.25rem 0.4rem !important; + border-radius: 3px; + width: fit-content; + margin: 0 auto; + } + .cq-rule-severity.error { background: #f8514922; color: var(--red); } + .cq-rule-severity.warning { background: #d2992222; color: var(--yellow); } + .cq-rule-severity.note { background: #3fb95022; color: var(--text); } + + .cq-footer { + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--border); + font-size: 0.75rem; + color: var(--muted); + } + .cq-footer a { color: var(--accent); text-decoration: none; } + .cq-footer a:hover { text-decoration: underline; } + /* ── Footer ───────────────────────────────────────────────── */ footer { max-width: 1400px; @@ -280,8 +398,36 @@
| Rule ID | +Count | +Severity | +
|---|