From 4a00472a83e24051561ed261d0a6b00ee454e7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Bautista=20Fern=C3=A1ndez?= Date: Fri, 29 May 2026 13:16:52 +0200 Subject: [PATCH] fix(ci): isolate critical flutter tests --- .gitea/workflows/build.yml | 103 ++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 20 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 8728e5f..1b73be6 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -30,7 +30,7 @@ jobs: - name: Analizar código run: flutter analyze --no-fatal-infos --no-fatal-warnings - - name: Ejecutar tests críticos + - name: Ejecutar tests criticos run: | python3 - <<'PY' import os @@ -43,37 +43,100 @@ jobs: 'test/servicios/servicio_programacion_alarmas_test.dart', 'test/estado/estado_alarmas_test.dart', ] - cmd = [ - 'flutter', - 'test', - '--no-pub', - '--concurrency=1', - '--timeout=60s', - '--reporter=expanded', - *test_paths, - ] 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(1) + time.sleep(2) os.killpg(pid, signal.SIGKILL) except ProcessLookupError: pass - print('$ ' + ' '.join(cmd), flush=True) - process = subprocess.Popen(cmd, start_new_session=use_process_group) - try: - returncode = process.wait(timeout=240) - except subprocess.TimeoutExpired: - print('ERROR: timeout ejecutando tests críticos', file=sys.stderr, flush=True) - kill_process_group(process.pid) - sys.exit(124) + 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 - sys.exit(returncode) + def run_test_file(path): + cmd = [ + 'flutter', + 'test', + '--no-pub', + '--concurrency=1', + '--timeout=30s', + '--reporter=expanded', + path, + ] + print('$ ' + ' '.join(cmd), flush=True) + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + start_new_session=use_process_group, + ) + try: + output, _ = process.communicate(timeout=120) + except subprocess.TimeoutExpired: + print(f'ERROR: timeout ejecutando test critico {path}', file=sys.stderr, flush=True) + kill_process_group(process.pid) + cleanup_flutter_processes() + return 124 + if output: + print(output, end='', flush=True) + cleanup_flutter_processes() + return process.returncode + + cleanup_flutter_processes() + for path in test_paths: + code = run_test_file(path) + if code != 0: + sys.exit(code) + sys.exit(0) PY - name: Limpiar procesos Flutter de tests