Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail

python scripts/validate_repo.py
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,34 @@ python -m compileall main.py src
```

O script remove marcadores de conflito e artefactos de branch comuns e cria backup automático (`src/app.py.bak`).
codex/review-repository-code-p61gr6


## Prevenção de conflitos (recomendado)

Ativa validação local automática antes de cada commit:

```bash
git config core.hooksPath .githooks
```

Ativa também opções de Git que ajudam em conflitos repetidos:

```bash
git config rerere.enabled true
git config merge.conflictstyle zdiff3
git config pull.rebase true
```

Fluxo recomendado para reduzir conflitos em `src/app.py`:

```bash
git checkout main
git pull origin main
git checkout -b fix/minha-alteracao
# editar
git add .
git commit -m "..."
git fetch origin
git rebase origin/main
```
37 changes: 37 additions & 0 deletions scripts/fix_conflict_artifacts.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,36 @@

from pathlib import Path
import argparse
codex/review-repository-code-p61gr6
import re
import shutil
import sys

CONFLICT_PREFIXES = ("<<<<<<<", "=======", ">>>>>>>")
codex/review-repository-code-p61gr6
CODEX_TOKEN = "codex/review-repository-code-"
BRANCH_LABEL_RE = re.compile(r"^(main|master|develop|release/.+|hotfix/.+)$")


def should_remove_line(lines: list[str], index: int) -> bool:
raw_line = lines[index]
stripped = raw_line.strip()
if not stripped:
return False

if stripped.startswith(CONFLICT_PREFIXES):
return True

if CODEX_TOKEN in stripped:
return True

if BRANCH_LABEL_RE.match(stripped):
before = lines[index - 1].strip() if index - 1 >= 0 else ""
after = lines[index + 1].strip() if index + 1 < len(lines) else ""
if CODEX_TOKEN in before or CODEX_TOKEN in after or any(
token in before or token in after for token in CONFLICT_PREFIXES
):
return True


def should_remove_line(raw_line: str) -> bool:
Expand All @@ -18,6 +44,7 @@ def should_remove_line(raw_line: str) -> bool:
return True
if stripped == "main":
return True

return False


Expand All @@ -26,8 +53,13 @@ def sanitize_file(path: Path, write: bool) -> tuple[int, int]:
cleaned: list[str] = []
removed = 0

codex/review-repository-code-p61gr6
for idx, line in enumerate(lines):
if should_remove_line(lines, idx):

for line in lines:
if should_remove_line(line):

removed += 1
continue
cleaned.append(line)
Expand All @@ -54,7 +86,12 @@ def main() -> int:
removed, total = sanitize_file(target, write=args.write)
if args.write:
if removed:
codex/review-repository-code-p61gr6
backup = target.with_suffix(target.suffix + ".bak")
print(f"Removidas {removed} linha(s) suspeita(s) de {total}. Backup criado em {backup}")

print(f"Removidas {removed} linha(s) suspeita(s) de {total}. Backup criado em {target.with_suffix(target.suffix + '.bak')}")

else:
print("Nenhuma linha suspeita encontrada. Nada para alterar.")
else:
Expand Down
56 changes: 45 additions & 11 deletions scripts/validate_repo.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,70 @@
from __future__ import annotations

from pathlib import Path
import ast
import compileall
import re
import sys

ROOT = Path(__file__).resolve().parents[1]
CONFLICT_TOKENS = ("<<<<<<<", "=======", ">>>>>>>")
CODEX_TOKEN = "codex/review-repository-code-"
BRANCH_LABEL_RE = re.compile(r"^(main|master|develop|release/.+|hotfix/.+)$")


def target_files() -> list[Path]:
return [ROOT / "main.py", *(ROOT / "src").rglob("*.py")]


def check_conflict_markers() -> list[str]:
problems: list[str] = []
targets = [ROOT / "main.py", *(ROOT / "src").rglob("*.py")]
for path in targets:
text = path.read_text(encoding="utf-8", errors="ignore")
for idx, line in enumerate(text.splitlines(), start=1):
if any(token in line for token in CONFLICT_TOKENS):
for path in target_files():
lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
for idx, raw in enumerate(lines, start=1):
stripped = raw.strip()
if any(token in raw for token in CONFLICT_TOKENS):
problems.append(f"{path.relative_to(ROOT)}:{idx}: marcador de conflito encontrado")
if line.strip().startswith("codex/review-repository-code-"):
continue

if CODEX_TOKEN in stripped:
problems.append(f"{path.relative_to(ROOT)}:{idx}: artefacto de branch encontrado")
continue

# Linha de branch "solta" típica de conflito mal resolvido.
if BRANCH_LABEL_RE.match(stripped):
before = lines[idx - 2].strip() if idx - 2 >= 0 else ""
after = lines[idx].strip() if idx < len(lines) else ""
if CODEX_TOKEN in before or CODEX_TOKEN in after or any(
tok in before or tok in after for tok in CONFLICT_TOKENS
):
problems.append(f"{path.relative_to(ROOT)}:{idx}: etiqueta de branch solta encontrada")
return problems


def check_python_syntax() -> list[str]:
errors: list[str] = []
for path in target_files():
source = path.read_text(encoding="utf-8", errors="ignore")
try:
ast.parse(source, filename=str(path.relative_to(ROOT)))
except SyntaxError as exc:
line = exc.lineno or 1
msg = exc.msg or "erro de sintaxe"
errors.append(f"{path.relative_to(ROOT)}:{line}: {msg}")
return errors


def main() -> int:
marker_errors = check_conflict_markers()
if marker_errors:
for error in marker_errors:
syntax_errors = check_python_syntax()

if marker_errors or syntax_errors:
for error in marker_errors + syntax_errors:
print(error)
return 1

ok = compileall.compile_dir(str(ROOT / "src"), quiet=1)
ok_src = compileall.compile_dir(str(ROOT / "src"), quiet=1)
ok_main = compileall.compile_file(str(ROOT / "main.py"), quiet=1)
if not ok or not ok_main:
if not ok_src or not ok_main:
print("Falha de compilação Python.")
return 1

Expand Down
26 changes: 13 additions & 13 deletions src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def _migrate_legacy_config_if_needed():
if CONFIG_FILE.exists() or not LEGACY_CONFIG_FILE.exists():
return

codex/review-repository-code-p61gr6
codex/review-repository-code-hpjdax

codex/review-repository-code-argiid
Expand All @@ -46,9 +47,6 @@ def _migrate_legacy_config_if_needed():

def _read_json_file(path: Path):
"""Le JSON de um ficheiro devolvendo dict vazio em caso de erro."""
try:

main
try:
legacy_data = json.loads(LEGACY_CONFIG_FILE.read_text(encoding="utf-8"))
APP_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
Expand All @@ -60,10 +58,11 @@ def _read_json_file(path: Path):
def _read_json_file(path: Path):
"""Le JSON de um ficheiro devolvendo dict vazio em caso de erro."""
try:
codex/review-repository-code-p61gr6

codex/review-repository-code-hpjdax


main
if path.exists():
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
Expand Down Expand Up @@ -142,6 +141,9 @@ def __init__(self):
self.config = load_config()
self.scan_paths = []
self._selection_loaded = False
codex/review-repository-code-p61gr6
self.filters_panel_visible = True

codex/review-repository-code-hpjdax
self.filters_panel_visible = True

Expand All @@ -155,7 +157,6 @@ def __init__(self):
self.filters_panel_visible = True


main

self.setWindowTitle("Duplicate File Cleaner")
self.setMinimumSize(900, 620)
Expand Down Expand Up @@ -416,6 +417,9 @@ def _build_scan_tab(self):
self.scan_mode.currentIndexChanged.connect(self._on_mode_change)
mode_layout.addWidget(self.scan_mode)
mode_layout.addStretch()
codex/review-repository-code-p61gr6


codex/review-repository-code-hpjdax


Expand All @@ -431,11 +435,13 @@ def _build_scan_tab(self):

codex/review-repository-code-l1cd2s

main

self.toggle_filters_btn = QPushButton("Esconder Selecao")
self.toggle_filters_btn.clicked.connect(self._toggle_filters_panel)
mode_layout.addWidget(self.toggle_filters_btn)
filter_layout.addLayout(mode_layout)
filter_layout.addLayout(mode_layout) codex/review-repository-code-p61gr6
self.filters_panel_widget = self._build_filters_panel()
filter_layout.addWidget(self.filters_panel_widget, 1)
codex/review-repository-code-hpjdax

self.filters_panel_widget = self._build_filters_panel()
Expand Down Expand Up @@ -472,7 +478,6 @@ def _build_scan_tab(self):
codex/review-repository-code-kf47vu
filters_panel_layout.addWidget(scroll, 1)
filter_layout.addWidget(scroll, 1)
main

quick_btn_layout = QHBoxLayout()
select_all_btn = QPushButton("Selecionar Tudo")
Expand All @@ -495,11 +500,6 @@ def _build_scan_tab(self):
filter_layout.addWidget(self.filters_panel_widget, 1)

filter_layout.addLayout(custom_layout)
main

main
main
main
scan_splitter.addWidget(filter_group)

progress_group = QGroupBox("Progresso")
Expand Down
Loading