codeclone 2.0.0b1__tar.gz → 2.0.0b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/PKG-INFO +3 -3
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/README.md +1 -1
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_css.py +71 -21
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_meta.py +19 -14
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone.egg-info/PKG-INFO +3 -3
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone.egg-info/requires.txt +1 -1
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/pyproject.toml +2 -2
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_html_report.py +90 -6
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_report.py +1 -1
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_report_contract_coverage.py +2 -2
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/LICENSE +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/__init__.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_args.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_baselines.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_config.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_gating.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_meta.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_paths.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_reports.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_rich.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_runtime.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_cli_summary.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_coerce.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_badges.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_data_attrs.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_escape.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_filters.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_js.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/__init__.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_assemble.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_components.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_context.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_glossary.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_icons.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/__init__.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_clones.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_coupling.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_dead_code.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_dependencies.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_overview.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_structural.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_sections/_suggestions.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_tables.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_report/_tabs.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_html_snippets.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/_schema_validation.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/baseline.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/blockhash.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/blocks.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/cache.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/cfg.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/cfg_model.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/cli.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/contracts.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/domain/__init__.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/domain/findings.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/domain/quality.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/domain/source_scope.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/errors.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/extractor.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/fingerprint.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/grouping.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/html_report.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/meta_markers.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/__init__.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/cohesion.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/complexity.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/coupling.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/dead_code.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/dependencies.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics/health.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/metrics_baseline.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/models.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/normalize.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/paths.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/pipeline.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/py.typed +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/__init__.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/_formatting.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/_source_kinds.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/blocks.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/derived.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/explain.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/explain_contract.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/findings.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/json_contract.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/markdown.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/merge.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/overview.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/sarif.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/segments.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/serialize.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/suggestions.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/report/types.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/scanner.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/structural_findings.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/suppressions.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/templates.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone/ui_messages.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone.egg-info/SOURCES.txt +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone.egg-info/dependency_links.txt +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone.egg-info/entry_points.txt +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/codeclone.egg-info/top_level.txt +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/setup.cfg +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_architecture.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_baseline.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_blockhash.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_blocks.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cache.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cfg.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cfg_model.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cli_config.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cli_inprocess.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cli_main_guard.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cli_main_guard_runpy.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cli_smoke.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_cli_unit.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_coerce.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_core_branch_coverage.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_detector_golden.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_extractor.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_fingerprint.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_golden_v2.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_html_report_helpers.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_init.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_metrics_baseline.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_metrics_modules.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_normalize.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_pipeline_metrics.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_pipeline_process.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_python_syntax_compat.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_report_branch_invariants.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_report_explain.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_report_source_kinds.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_report_suggestions.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_scanner_extra.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_security.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_segments.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_structural_findings.py +0 -0
- {codeclone-2.0.0b1 → codeclone-2.0.0b2}/tests/test_suppressions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeclone
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0b2
|
|
4
4
|
Summary: Structural code quality analysis for Python
|
|
5
5
|
Author-email: Den Rozhnovskiy <pytelemonbot@mail.ru>
|
|
6
6
|
Maintainer-email: Den Rozhnovskiy <pytelemonbot@mail.ru>
|
|
@@ -36,7 +36,7 @@ Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
|
36
36
|
Requires-Dist: build>=1.4.1; extra == "dev"
|
|
37
37
|
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
38
38
|
Requires-Dist: mypy>=1.19.1; extra == "dev"
|
|
39
|
-
Requires-Dist: ruff>=0.15.
|
|
39
|
+
Requires-Dist: ruff>=0.15.8; extra == "dev"
|
|
40
40
|
Requires-Dist: pre-commit>=4.5.1; extra == "dev"
|
|
41
41
|
Dynamic: license-file
|
|
42
42
|
|
|
@@ -239,7 +239,7 @@ Dynamic/runtime false positives are resolved via explicit inline suppressions, n
|
|
|
239
239
|
{
|
|
240
240
|
"report_schema_version": "2.1",
|
|
241
241
|
"meta": {
|
|
242
|
-
"codeclone_version": "2.0.
|
|
242
|
+
"codeclone_version": "2.0.0b2",
|
|
243
243
|
"project_name": "...",
|
|
244
244
|
"scan_root": ".",
|
|
245
245
|
"report_mode": "full",
|
|
@@ -197,7 +197,7 @@ Dynamic/runtime false positives are resolved via explicit inline suppressions, n
|
|
|
197
197
|
{
|
|
198
198
|
"report_schema_version": "2.1",
|
|
199
199
|
"meta": {
|
|
200
|
-
"codeclone_version": "2.0.
|
|
200
|
+
"codeclone_version": "2.0.0b2",
|
|
201
201
|
"project_name": "...",
|
|
202
202
|
"scan_root": ".",
|
|
203
203
|
"report_mode": "full",
|
|
@@ -139,15 +139,18 @@ _LAYOUT = """\
|
|
|
139
139
|
box-shadow:var(--shadow-sm)}
|
|
140
140
|
.topbar-inner{display:flex;align-items:center;justify-content:space-between;
|
|
141
141
|
height:72px;padding:0 var(--sp-6);max-width:var(--container-max);margin:0 auto}
|
|
142
|
-
.brand{display:flex;align-items:center;gap:var(--sp-3)}
|
|
142
|
+
.brand{display:flex;align-items:center;gap:var(--sp-3);min-width:0;flex:1}
|
|
143
143
|
.brand-logo{flex-shrink:0}
|
|
144
|
-
.brand-text{display:flex;flex-direction:column;gap:2px}
|
|
145
|
-
.brand h1{
|
|
146
|
-
|
|
147
|
-
.brand-
|
|
144
|
+
.brand-text{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
|
|
145
|
+
.brand h1{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-1);font-size:1.15rem;
|
|
146
|
+
font-weight:700;color:var(--text-primary);line-height:1.3;min-width:0}
|
|
147
|
+
.brand-meta{font-size:.78rem;color:var(--text-muted);overflow-wrap:anywhere}
|
|
148
|
+
.brand-project{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:4px;
|
|
149
|
+
font-weight:500;color:var(--text-secondary);min-width:0}
|
|
148
150
|
.brand-project-name{font-family:var(--font-mono);font-size:.85em;font-weight:500;padding:1px 5px;
|
|
149
|
-
border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--accent-primary)
|
|
150
|
-
|
|
151
|
+
border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--accent-primary);
|
|
152
|
+
max-width:100%;overflow-wrap:anywhere}
|
|
153
|
+
.topbar-actions{display:flex;align-items:center;gap:var(--sp-2);flex-shrink:0;flex-wrap:wrap}
|
|
151
154
|
|
|
152
155
|
/* Theme toggle */
|
|
153
156
|
.theme-toggle{display:inline-flex;align-items:center;gap:var(--sp-1);
|
|
@@ -284,9 +287,15 @@ _INSIGHT = """\
|
|
|
284
287
|
# ---------------------------------------------------------------------------
|
|
285
288
|
|
|
286
289
|
_TABLES = """\
|
|
287
|
-
.table-wrap{
|
|
288
|
-
margin-bottom:var(--sp-4)
|
|
289
|
-
|
|
290
|
+
.table-wrap{display:block;inline-size:100%;max-inline-size:100%;min-inline-size:0;overflow-x:auto;
|
|
291
|
+
overflow-y:hidden;border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:var(--sp-4);
|
|
292
|
+
background:
|
|
293
|
+
linear-gradient(to right,var(--bg-surface) 30%,transparent) left center / 40px 100% no-repeat local,
|
|
294
|
+
linear-gradient(to left,var(--bg-surface) 30%,transparent) right center / 40px 100% no-repeat local,
|
|
295
|
+
linear-gradient(to right,rgba(0,0,0,.15),transparent) left center / 14px 100% no-repeat scroll,
|
|
296
|
+
linear-gradient(to left,rgba(0,0,0,.15),transparent) right center / 14px 100% no-repeat scroll}
|
|
297
|
+
.table{inline-size:max-content;min-inline-size:100%;border-collapse:collapse;font-size:.82rem;
|
|
298
|
+
font-family:var(--font-mono)}
|
|
290
299
|
.table th{position:sticky;top:0;z-index:2;padding:var(--sp-2) var(--sp-3);text-align:left;font-family:var(--font-sans);
|
|
291
300
|
font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;
|
|
292
301
|
color:var(--text-muted);background:var(--bg-overlay);border-bottom:1px solid var(--border);
|
|
@@ -300,10 +309,11 @@ _TABLES = """\
|
|
|
300
309
|
.table tr:last-child td{border-bottom:none}
|
|
301
310
|
.table tr:hover td{background:var(--bg-raised)}
|
|
302
311
|
.table .col-name{font-weight:500;color:var(--text-primary)}
|
|
303
|
-
.table .col-file{color:var(--text-muted);max-width:240px;overflow:hidden;
|
|
312
|
+
.table .col-file,.table .col-path{color:var(--text-muted);max-width:240px;overflow:hidden;
|
|
304
313
|
text-overflow:ellipsis;white-space:nowrap}
|
|
305
|
-
.table .col-number{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
|
|
306
|
-
.table .col-risk{white-space:nowrap}
|
|
314
|
+
.table .col-number,.table .col-num{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
|
|
315
|
+
.table .col-risk,.table .col-badge,.table .col-cat{white-space:nowrap}
|
|
316
|
+
.table .col-steps{max-width:120px;word-break:break-word}
|
|
307
317
|
.table .col-wide{max-width:320px;word-break:break-all}
|
|
308
318
|
.table-empty{padding:var(--sp-8);text-align:center;color:var(--text-muted);font-size:.9rem}
|
|
309
319
|
"""
|
|
@@ -478,6 +488,12 @@ _OVERVIEW = """\
|
|
|
478
488
|
}
|
|
479
489
|
@media(max-width:520px){
|
|
480
490
|
.overview-kpi-cards{grid-template-columns:1fr}
|
|
491
|
+
.overview-kpi-cards .meta-item{grid-template-rows:auto auto auto;align-content:start;
|
|
492
|
+
min-height:0}
|
|
493
|
+
.overview-kpi-cards .meta-item .meta-label{min-height:0}
|
|
494
|
+
.overview-kpi-cards .meta-item .meta-value{padding-top:0}
|
|
495
|
+
.overview-kpi-cards .kpi-detail{align-self:start}
|
|
496
|
+
.overview-kpi-cards .kpi-micro{max-width:100%;white-space:normal;overflow-wrap:anywhere}
|
|
481
497
|
}
|
|
482
498
|
|
|
483
499
|
/* Health gauge */
|
|
@@ -510,7 +526,7 @@ _OVERVIEW = """\
|
|
|
510
526
|
background:var(--bg-alt)}
|
|
511
527
|
|
|
512
528
|
/* Badge modal */
|
|
513
|
-
.badge-modal{max-width:
|
|
529
|
+
.badge-modal{max-width:680px;width:92vw;max-height:85vh}
|
|
514
530
|
.badge-modal .modal-head{display:flex;align-items:center;justify-content:space-between;
|
|
515
531
|
padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--border)}
|
|
516
532
|
.badge-modal .modal-head h2{font-size:1rem;font-weight:700;margin:0}
|
|
@@ -916,12 +932,19 @@ _META_PANEL = """\
|
|
|
916
932
|
/* Provenance summary badges */
|
|
917
933
|
.prov-summary{display:flex;flex-wrap:wrap;align-items:center;gap:6px;
|
|
918
934
|
padding:var(--sp-2) var(--sp-4);border-top:1px solid var(--border)}
|
|
919
|
-
.prov-badge{
|
|
920
|
-
border-radius:
|
|
921
|
-
|
|
922
|
-
.prov-badge
|
|
923
|
-
.prov-badge
|
|
924
|
-
.prov-badge
|
|
935
|
+
.prov-badge{display:inline-flex;align-items:center;gap:4px;font-size:.66rem;
|
|
936
|
+
padding:2px 8px;border-radius:var(--radius-sm);background:var(--bg-raised);
|
|
937
|
+
white-space:nowrap;line-height:1.3;border:1px solid color-mix(in srgb,var(--border) 55%,transparent)}
|
|
938
|
+
.prov-badge-val{font-weight:600;font-variant-numeric:tabular-nums}
|
|
939
|
+
.prov-badge-lbl{font-weight:400;color:var(--text-muted);text-transform:lowercase}
|
|
940
|
+
.prov-badge--green{background:var(--success-muted);border-color:color-mix(in srgb,var(--success) 20%,transparent)}
|
|
941
|
+
.prov-badge--green .prov-badge-val{color:var(--success)}
|
|
942
|
+
.prov-badge--red{background:var(--error-muted);border-color:color-mix(in srgb,var(--error) 20%,transparent)}
|
|
943
|
+
.prov-badge--red .prov-badge-val{color:var(--error)}
|
|
944
|
+
.prov-badge--amber{background:var(--warning-muted);border-color:color-mix(in srgb,var(--warning) 20%,transparent)}
|
|
945
|
+
.prov-badge--amber .prov-badge-val{color:var(--warning)}
|
|
946
|
+
.prov-badge--neutral{background:var(--bg-overlay);border-color:color-mix(in srgb,var(--border) 75%,transparent)}
|
|
947
|
+
.prov-badge--neutral .prov-badge-val{color:var(--text-secondary)}
|
|
925
948
|
.prov-explain{font-size:.62rem;color:var(--text-muted);margin-left:auto;font-style:italic}
|
|
926
949
|
"""
|
|
927
950
|
|
|
@@ -1033,12 +1056,39 @@ _UTILITY = """\
|
|
|
1033
1056
|
.overview-row-spread{margin-left:0;width:100%}
|
|
1034
1057
|
.suggestion-head{flex-direction:column;align-items:flex-start}
|
|
1035
1058
|
.suggestion-facts{grid-template-columns:1fr}
|
|
1059
|
+
.sf-head{flex-direction:column;align-items:flex-start}
|
|
1060
|
+
.sf-meta{width:100%}
|
|
1036
1061
|
.container{padding:0 var(--sp-3)}
|
|
1037
|
-
.
|
|
1062
|
+
.topbar{position:static}
|
|
1063
|
+
.topbar-inner{height:auto;padding:var(--sp-2) var(--sp-3);flex-direction:row;
|
|
1064
|
+
align-items:center;gap:var(--sp-2)}
|
|
1065
|
+
.brand{flex:1;min-width:0;align-items:center;gap:var(--sp-2)}
|
|
1066
|
+
.brand-logo{width:24px;height:24px}
|
|
1067
|
+
.brand-text{gap:0}
|
|
1068
|
+
.brand h1{font-size:.85rem;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
1069
|
+
.brand-project-name{font-size:.78em;padding:0 3px}
|
|
1070
|
+
.brand-meta{display:none}
|
|
1071
|
+
.topbar-actions{flex-shrink:0;gap:var(--sp-1)}
|
|
1072
|
+
.topbar-actions .btn-prov{font-size:0;gap:0;width:32px;height:32px;
|
|
1073
|
+
padding:0;align-items:center;justify-content:center}
|
|
1074
|
+
.topbar-actions .btn-prov .prov-dot{width:10px;height:10px}
|
|
1075
|
+
.theme-toggle{font-size:0;gap:0;width:32px;height:32px;
|
|
1076
|
+
padding:0;align-items:center;justify-content:center}
|
|
1077
|
+
.theme-toggle svg{width:16px;height:16px}
|
|
1078
|
+
.main-tabs-wrap{position:sticky;top:0;z-index:90;padding:var(--sp-2) 0 0}
|
|
1079
|
+
.main-tabs{padding:var(--sp-1);gap:2px;
|
|
1080
|
+
background:
|
|
1081
|
+
linear-gradient(to right,var(--bg-surface) 30%,transparent) left center / 28px 100% no-repeat local,
|
|
1082
|
+
linear-gradient(to left,var(--bg-surface) 30%,transparent) right center / 28px 100% no-repeat local,
|
|
1083
|
+
linear-gradient(to right,rgba(0,0,0,.12),transparent) left center / 10px 100% no-repeat scroll,
|
|
1084
|
+
linear-gradient(to left,rgba(0,0,0,.12),transparent) right center / 10px 100% no-repeat scroll,
|
|
1085
|
+
var(--bg-surface)}
|
|
1086
|
+
.main-tab{flex:none;padding:var(--sp-1) var(--sp-2);font-size:.78rem}
|
|
1038
1087
|
}
|
|
1039
1088
|
@media(max-width:480px){
|
|
1040
1089
|
.overview-kpi-grid{grid-template-columns:1fr}
|
|
1041
1090
|
.search-box input[type="text"]{width:140px}
|
|
1091
|
+
.brand-logo{width:28px;height:28px}
|
|
1042
1092
|
}
|
|
1043
1093
|
|
|
1044
1094
|
/* Print */
|
|
@@ -333,36 +333,41 @@ def render_meta_panel(ctx: ReportContext) -> str:
|
|
|
333
333
|
_section_html(st, rows) for st, rows in meta_sections if rows
|
|
334
334
|
)
|
|
335
335
|
|
|
336
|
-
def _prov_badge(label: str, color: str) -> str:
|
|
337
|
-
return
|
|
336
|
+
def _prov_badge(label: str, value: str, color: str) -> str:
|
|
337
|
+
return (
|
|
338
|
+
f'<span class="prov-badge prov-badge--{color}">'
|
|
339
|
+
f'<span class="prov-badge-val">{_escape_html(value)}</span>'
|
|
340
|
+
f'<span class="prov-badge-lbl">{_escape_html(label)}</span>'
|
|
341
|
+
"</span>"
|
|
342
|
+
)
|
|
338
343
|
|
|
339
344
|
badges: list[str] = []
|
|
340
345
|
if _bl_verified is True:
|
|
341
|
-
badges.append(_prov_badge("Baseline verified", "green"))
|
|
346
|
+
badges.append(_prov_badge("Baseline", "verified", "green"))
|
|
342
347
|
elif _bl_loaded is True and _bl_verified is not True:
|
|
343
|
-
badges.append(_prov_badge("Baseline untrusted", "red"))
|
|
348
|
+
badges.append(_prov_badge("Baseline", "untrusted", "red"))
|
|
344
349
|
elif _bl_loaded is False or _bl_loaded is None:
|
|
345
|
-
badges.append(_prov_badge("Baseline missing", "amber"))
|
|
350
|
+
badges.append(_prov_badge("Baseline", "missing", "amber"))
|
|
346
351
|
if ctx.report_schema_version:
|
|
347
|
-
badges.append(_prov_badge(
|
|
352
|
+
badges.append(_prov_badge("Schema", str(ctx.report_schema_version), "neutral"))
|
|
348
353
|
if _bl_fp_ver is not None:
|
|
349
|
-
badges.append(_prov_badge(
|
|
354
|
+
badges.append(_prov_badge("Fingerprint", str(_bl_fp_ver), "neutral"))
|
|
350
355
|
gen_name = str(_bl_gen_name or "")
|
|
351
356
|
if gen_name and gen_name != "codeclone":
|
|
352
|
-
badges.append(_prov_badge(
|
|
357
|
+
badges.append(_prov_badge("Generator mismatch", gen_name, "red"))
|
|
353
358
|
if _cache_used is True:
|
|
354
|
-
badges.append(_prov_badge("Cache hit", "green"))
|
|
359
|
+
badges.append(_prov_badge("Cache", "hit", "green"))
|
|
355
360
|
elif _cache_used is False:
|
|
356
|
-
badges.append(_prov_badge("Cache miss", "amber"))
|
|
361
|
+
badges.append(_prov_badge("Cache", "miss", "amber"))
|
|
357
362
|
else:
|
|
358
|
-
badges.append(_prov_badge("Cache N/A", "neutral"))
|
|
363
|
+
badges.append(_prov_badge("Cache", "N/A", "neutral"))
|
|
359
364
|
analysis_mode = str(_meta_pick(meta.get("analysis_mode")) or "")
|
|
360
365
|
if analysis_mode:
|
|
361
|
-
badges.append(_prov_badge(
|
|
366
|
+
badges.append(_prov_badge("Mode", analysis_mode, "neutral"))
|
|
362
367
|
if _mbl_verified is True:
|
|
363
|
-
badges.append(_prov_badge("Metrics baseline verified", "green"))
|
|
368
|
+
badges.append(_prov_badge("Metrics baseline", "verified", "green"))
|
|
364
369
|
elif _mbl_loaded is True and _mbl_verified is not True:
|
|
365
|
-
badges.append(_prov_badge("Metrics baseline untrusted", "red"))
|
|
370
|
+
badges.append(_prov_badge("Metrics baseline", "untrusted", "red"))
|
|
366
371
|
|
|
367
372
|
prov_summary = (
|
|
368
373
|
f'<div class="prov-summary">{"".join(badges)}'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codeclone
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0b2
|
|
4
4
|
Summary: Structural code quality analysis for Python
|
|
5
5
|
Author-email: Den Rozhnovskiy <pytelemonbot@mail.ru>
|
|
6
6
|
Maintainer-email: Den Rozhnovskiy <pytelemonbot@mail.ru>
|
|
@@ -36,7 +36,7 @@ Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
|
36
36
|
Requires-Dist: build>=1.4.1; extra == "dev"
|
|
37
37
|
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
38
38
|
Requires-Dist: mypy>=1.19.1; extra == "dev"
|
|
39
|
-
Requires-Dist: ruff>=0.15.
|
|
39
|
+
Requires-Dist: ruff>=0.15.8; extra == "dev"
|
|
40
40
|
Requires-Dist: pre-commit>=4.5.1; extra == "dev"
|
|
41
41
|
Dynamic: license-file
|
|
42
42
|
|
|
@@ -239,7 +239,7 @@ Dynamic/runtime false positives are resolved via explicit inline suppressions, n
|
|
|
239
239
|
{
|
|
240
240
|
"report_schema_version": "2.1",
|
|
241
241
|
"meta": {
|
|
242
|
-
"codeclone_version": "2.0.
|
|
242
|
+
"codeclone_version": "2.0.0b2",
|
|
243
243
|
"project_name": "...",
|
|
244
244
|
"scan_root": ".",
|
|
245
245
|
"report_mode": "full",
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "codeclone"
|
|
7
|
-
version = "2.0.
|
|
7
|
+
version = "2.0.0b2"
|
|
8
8
|
description = "Structural code quality analysis for Python"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = "MIT"
|
|
@@ -67,7 +67,7 @@ dev = [
|
|
|
67
67
|
"build>=1.4.1",
|
|
68
68
|
"twine>=5.0.0",
|
|
69
69
|
"mypy>=1.19.1",
|
|
70
|
-
"ruff>=0.15.
|
|
70
|
+
"ruff>=0.15.8",
|
|
71
71
|
"pre-commit>=4.5.1",
|
|
72
72
|
]
|
|
73
73
|
|
|
@@ -746,6 +746,54 @@ def test_html_report_topbar_actions_present() -> None:
|
|
|
746
746
|
assert 'id="help-modal"' not in html
|
|
747
747
|
|
|
748
748
|
|
|
749
|
+
def test_html_report_mobile_topbar_reflows_brand_block() -> None:
|
|
750
|
+
html = build_html_report(func_groups={}, block_groups={}, segment_groups={})
|
|
751
|
+
_assert_html_contains(
|
|
752
|
+
html,
|
|
753
|
+
"@media(max-width:768px){",
|
|
754
|
+
".topbar{position:static}",
|
|
755
|
+
".topbar-inner{height:auto;",
|
|
756
|
+
".brand-meta{display:none}",
|
|
757
|
+
".main-tabs-wrap{position:sticky;top:0;",
|
|
758
|
+
".main-tab{flex:none;",
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def test_html_report_narrow_kpi_cards_keep_badges_inside_card() -> None:
|
|
763
|
+
html = build_html_report(func_groups={}, block_groups={}, segment_groups={})
|
|
764
|
+
_assert_html_contains(
|
|
765
|
+
html,
|
|
766
|
+
"@media(max-width:520px){",
|
|
767
|
+
(
|
|
768
|
+
".overview-kpi-cards .meta-item{grid-template-rows:auto auto auto;"
|
|
769
|
+
"align-content:start;"
|
|
770
|
+
),
|
|
771
|
+
".overview-kpi-cards .kpi-detail{align-self:start}",
|
|
772
|
+
(
|
|
773
|
+
".overview-kpi-cards .kpi-micro{max-width:100%;white-space:normal;"
|
|
774
|
+
"overflow-wrap:anywhere}"
|
|
775
|
+
),
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def test_html_report_table_css_matches_rendered_column_classes() -> None:
|
|
780
|
+
html = build_html_report(func_groups={}, block_groups={}, segment_groups={})
|
|
781
|
+
_assert_html_contains(
|
|
782
|
+
html,
|
|
783
|
+
".table-wrap{display:block;inline-size:100%;max-inline-size:100%;min-inline-size:0;overflow-x:auto;",
|
|
784
|
+
".table{inline-size:max-content;min-inline-size:100%;border-collapse:collapse;",
|
|
785
|
+
(
|
|
786
|
+
".table .col-file,.table .col-path{color:var(--text-muted);"
|
|
787
|
+
"max-width:240px;overflow:hidden;"
|
|
788
|
+
),
|
|
789
|
+
(
|
|
790
|
+
".table .col-number,.table .col-num{font-variant-numeric:"
|
|
791
|
+
"tabular-nums;text-align:right;white-space:nowrap}"
|
|
792
|
+
),
|
|
793
|
+
".table .col-risk,.table .col-badge,.table .col-cat{white-space:nowrap}",
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
|
|
749
797
|
def test_html_report_footer_links_present() -> None:
|
|
750
798
|
html = build_html_report(func_groups={}, block_groups={}, segment_groups={})
|
|
751
799
|
assert f'href="{REPOSITORY_URL}"' in html
|
|
@@ -809,6 +857,31 @@ def test_html_report_includes_provenance_metadata(
|
|
|
809
857
|
assert "deterministic render" not in html
|
|
810
858
|
|
|
811
859
|
|
|
860
|
+
def test_html_report_provenance_summary_uses_card_like_badges(
|
|
861
|
+
report_meta_factory: Callable[..., dict[str, object]],
|
|
862
|
+
) -> None:
|
|
863
|
+
html = build_html_report(
|
|
864
|
+
func_groups={},
|
|
865
|
+
block_groups={},
|
|
866
|
+
segment_groups={},
|
|
867
|
+
report_meta=report_meta_factory(
|
|
868
|
+
baseline_schema_version=2,
|
|
869
|
+
baseline_fingerprint_version=1,
|
|
870
|
+
),
|
|
871
|
+
)
|
|
872
|
+
_assert_html_contains(
|
|
873
|
+
html,
|
|
874
|
+
'class="prov-badge prov-badge--green"',
|
|
875
|
+
'class="prov-badge prov-badge--neutral"',
|
|
876
|
+
'<span class="prov-badge-val">verified</span>',
|
|
877
|
+
'<span class="prov-badge-lbl">Baseline</span>',
|
|
878
|
+
'<span class="prov-badge-val">2.1</span>',
|
|
879
|
+
'<span class="prov-badge-lbl">Schema</span>',
|
|
880
|
+
'<span class="prov-badge-val">1</span>',
|
|
881
|
+
'<span class="prov-badge-lbl">Fingerprint</span>',
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
|
|
812
885
|
def test_html_report_escapes_meta_and_title(
|
|
813
886
|
tmp_path: Path,
|
|
814
887
|
report_meta_factory: Callable[..., dict[str, object]],
|
|
@@ -1712,10 +1785,17 @@ def test_html_report_provenance_badges_cover_mismatch_and_untrusted_metrics() ->
|
|
|
1712
1785
|
"baseline_fingerprint_version": "1",
|
|
1713
1786
|
},
|
|
1714
1787
|
)
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1788
|
+
_assert_html_contains(
|
|
1789
|
+
html,
|
|
1790
|
+
'<span class="prov-badge-val">missing</span>',
|
|
1791
|
+
'<span class="prov-badge-lbl">Baseline</span>',
|
|
1792
|
+
'<span class="prov-badge-val">other-generator</span>',
|
|
1793
|
+
'<span class="prov-badge-lbl">Generator mismatch</span>',
|
|
1794
|
+
'<span class="prov-badge-val">untrusted</span>',
|
|
1795
|
+
'<span class="prov-badge-lbl">Metrics baseline</span>',
|
|
1796
|
+
'<span class="prov-badge-val">N/A</span>',
|
|
1797
|
+
'<span class="prov-badge-lbl">Cache</span>',
|
|
1798
|
+
)
|
|
1719
1799
|
|
|
1720
1800
|
|
|
1721
1801
|
def test_html_report_provenance_handles_non_boolean_baseline_loaded() -> None:
|
|
@@ -1729,8 +1809,12 @@ def test_html_report_provenance_handles_non_boolean_baseline_loaded() -> None:
|
|
|
1729
1809
|
"report_schema_version": "2.0",
|
|
1730
1810
|
},
|
|
1731
1811
|
)
|
|
1732
|
-
|
|
1733
|
-
|
|
1812
|
+
_assert_html_contains(
|
|
1813
|
+
html,
|
|
1814
|
+
'<span class="prov-badge-val">2.0</span>',
|
|
1815
|
+
'<span class="prov-badge-lbl">Schema</span>',
|
|
1816
|
+
)
|
|
1817
|
+
assert '<span class="prov-badge-lbl">Baseline</span>' not in html
|
|
1734
1818
|
|
|
1735
1819
|
|
|
1736
1820
|
def test_html_report_dependency_hubs_deterministic_tie_order() -> None:
|
|
@@ -491,7 +491,7 @@ def test_report_sarif_uses_representative_and_related_locations() -> None:
|
|
|
491
491
|
}
|
|
492
492
|
sarif_payload = json.loads(
|
|
493
493
|
to_sarif_report(
|
|
494
|
-
meta={"codeclone_version": "2.0.
|
|
494
|
+
meta={"codeclone_version": "2.0.0b2", "scan_root": "/repo"},
|
|
495
495
|
func_groups=groups,
|
|
496
496
|
block_groups={},
|
|
497
497
|
segment_groups={},
|
|
@@ -427,7 +427,7 @@ def _rich_report_document() -> dict[str, object]:
|
|
|
427
427
|
),
|
|
428
428
|
)
|
|
429
429
|
meta = {
|
|
430
|
-
"codeclone_version": "2.0.
|
|
430
|
+
"codeclone_version": "2.0.0b2",
|
|
431
431
|
"project_name": "codeclone",
|
|
432
432
|
"scan_root": "/repo/codeclone",
|
|
433
433
|
"python_version": "3.13.11",
|
|
@@ -1251,7 +1251,7 @@ def test_render_sarif_report_document_without_srcroot_keeps_relative_payload() -
|
|
|
1251
1251
|
payload = {
|
|
1252
1252
|
"report_schema_version": "2.1",
|
|
1253
1253
|
"meta": {
|
|
1254
|
-
"codeclone_version": "2.0.
|
|
1254
|
+
"codeclone_version": "2.0.0b2",
|
|
1255
1255
|
"analysis_mode": "ci",
|
|
1256
1256
|
"report_mode": "full",
|
|
1257
1257
|
"runtime": {},
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|