From 20c135a848ccd6cae47421b3cd6e6987c4aedb9d Mon Sep 17 00:00:00 2001 From: FreeTLab Date: Sun, 31 May 2026 00:08:01 +0200 Subject: [PATCH] fix(ci): simplify Flutter test execution - Remove complex Python wrapper that caused timeout issues - Use direct flutter test with --concurrency=1 --timeout=60s - Simplify cleanup with pkill instead of Python loop - Increase timeout from 4m to 15m (tests take ~10s but CI overhead is high) --- .gitea/workflows/build.yml | 210 +------------------------------------ 1 file changed, 3 insertions(+), 207 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index f8ad2bf..73fdcc1 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -31,217 +31,13 @@ jobs: run: flutter analyze --no-fatal-infos --no-fatal-warnings - name: Ejecutar tests criticos - timeout-minutes: 4 + timeout-minutes: 15 run: | - python3 - <<'PY' - import os - import signal - import subprocess - import sys - import tempfile - import time - - test_paths = [ - 'test/servicios/servicio_programacion_alarmas_test.dart', - 'test/estado/estado_alarmas_test.dart', - ] - use_process_group = hasattr(os, 'killpg') and os.name != 'nt' - workspace = os.getcwd() - process_patterns = ( - 'flutter_tester', - 'flutter_tools.snapshot', - 'frontend_server', - 'dartvm', - 'dartaotruntime', - ) - - def kill_process_group(pid): - if not use_process_group: - return - try: - os.killpg(pid, signal.SIGTERM) - time.sleep(2) - os.killpg(pid, signal.SIGKILL) - except ProcessLookupError: - pass - - def cleanup_flutter_processes(): - ps = subprocess.run( - ['ps', '-axww', '-o', 'pid=', '-o', 'command='], - text=True, - stdout=subprocess.PIPE, - check=False, - ) - current_pid = os.getpid() - killed = [] - for raw in ps.stdout.splitlines(): - line = raw.strip() - if not line: - continue - pid_text, _, command = line.partition(' ') - try: - pid = int(pid_text) - except ValueError: - continue - if pid == current_pid: - continue - if workspace not in command and not any(path in command for path in test_paths): - continue - if not any(pattern in command for pattern in process_patterns + tuple(test_paths)): - continue - try: - os.kill(pid, signal.SIGTERM) - killed.append(pid) - except ProcessLookupError: - pass - if killed: - print('Procesos Flutter de test terminados: ' + ', '.join(map(str, killed)), flush=True) - time.sleep(1) - for pid in killed: - try: - os.kill(pid, signal.SIGKILL) - except ProcessLookupError: - pass - - def run_test_file(path): - cmd = [ - 'flutter', - 'test', - '--no-pub', - '--concurrency=1', - '--timeout=30s', - '--reporter=expanded', - path, - ] - print('$ ' + ' '.join(cmd), flush=True) - output_path = None - try: - with tempfile.NamedTemporaryFile(delete=False) as tmp: - output_path = tmp.name - with open(output_path, 'w', encoding='utf-8', errors='replace') as writer: - process = subprocess.Popen( - cmd, - stdout=writer, - stderr=subprocess.STDOUT, - start_new_session=use_process_group, - ) - deadline = time.monotonic() + 90 - success_deadline = None - - def flush_new_output(): - nonlocal success_deadline - with open(output_path, 'r', encoding='utf-8', errors='replace') as reader: - reader.seek(flush_new_output.offset) - chunk = reader.read() - flush_new_output.offset = reader.tell() - if not chunk: - return - print(chunk, end='', flush=True) - if 'All tests passed!' in chunk and success_deadline is None: - success_deadline = time.monotonic() + 5 - - flush_new_output.offset = 0 - - while True: - flush_new_output() - - returncode = process.poll() - if returncode is not None: - flush_new_output() - cleanup_flutter_processes() - return returncode - if success_deadline is not None and time.monotonic() >= success_deadline: - print( - f'WARN: {path} informó éxito pero Flutter no cerró; terminando proceso residual', - flush=True, - ) - kill_process_group(process.pid) - flush_new_output() - cleanup_flutter_processes() - return 0 - if time.monotonic() >= deadline: - print(f'ERROR: timeout ejecutando test critico {path}', file=sys.stderr, flush=True) - kill_process_group(process.pid) - flush_new_output() - cleanup_flutter_processes() - return 124 - time.sleep(1) - finally: - if output_path: - try: - os.unlink(output_path) - except FileNotFoundError: - pass - - cleanup_flutter_processes() - for path in test_paths: - code = run_test_file(path) - if code != 0: - sys.exit(code) - sys.exit(0) - PY + flutter test test/servicios/servicio_programacion_alarmas_test.dart test/estado/estado_alarmas_test.dart --concurrency=1 --timeout=60s - name: Limpiar procesos Flutter de tests if: always() - run: | - python3 - <<'PY' - import os - import signal - import subprocess - import time - - workspace = os.getcwd() - test_paths = ( - 'test/estado/estado_alarmas_test.dart', - 'test/servicios/servicio_programacion_alarmas_test.dart', - ) - patterns = ( - 'flutter_tester', - 'flutter_tools.snapshot', - 'frontend_server', - 'dartvm', - 'dartaotruntime', - ) + test_paths - current_pid = os.getpid() - ps = subprocess.run( - ['ps', '-axww', '-o', 'pid=', '-o', 'command='], - text=True, - stdout=subprocess.PIPE, - check=False, - ) - killed = [] - for raw in ps.stdout.splitlines(): - line = raw.strip() - if not line: - continue - pid_text, _, command = line.partition(' ') - try: - pid = int(pid_text) - except ValueError: - continue - if pid == current_pid: - continue - if workspace not in command and not any(path in command for path in test_paths): - continue - if not any(pattern in command for pattern in patterns): - continue - try: - os.kill(pid, signal.SIGTERM) - killed.append(pid) - except ProcessLookupError: - pass - - if killed: - print('Procesos Flutter de test terminados: ' + ', '.join(map(str, killed))) - time.sleep(1) - for pid in killed: - try: - os.kill(pid, signal.SIGKILL) - except ProcessLookupError: - pass - else: - print('No quedaron procesos Flutter de test vivos') - PY + run: pkill -f 'flutter_tester|flutter_tools.snapshot|dartaotruntime' 2>/dev/null || true build: name: Build APK + AAB release