Commits

El botón «Actualizar» ya actualiza y el conector se actualiza solo

Cerramos el círculo de las actualizaciones del panel: el refresco del inventario pasa a ser real, la actualización de núcleo vuelve a funcionar con WordPress 7.1 y el panel empuja el conector a las webs sin entrar en ningún wp-admin.

1e29fc6 refresco del inventario con cola y progreso; b0749c9 fix Core_Upgrader en WP 7.1 (conector 1.12.0); 70bf60d auto-actualización del conector por el panel (conector 1.13.0) · wp-mantenimiento · milestone

Llevábamos tiempo con un botón «Actualizar» que no actualizaba nada: reproducía la última foto del inventario, aunque la foto tuviera hasta dos horas. Hoy hemos cerrado ese círculo por los dos extremos: el panel refresca de verdad cuando le pides, y además ya empuja las actualizaciones del conector a las webs sin entrar en ningún wp-admin.

Un botón que pintaba acción

El handler del «Actualizar» era, literalmente, una lectura de la base de datos del propio panel. Ninguna llamada a WordPress. Debajo había tres capas encadenadas: el worker solo refrescaba webs con la caché de más de dos horas, actualizar un plugin o el núcleo no invalidaba nada, y las columnas de versiones del listado solo las escribía la acción «Verificar». El conector, irónicamente, era el honesto: su /info iba siempre en vivo. El listado y el workspace contaban dos verdades distintas de la misma web.

Lo nuevo: un servicio único de refresco que usan el worker y el botón, así que las dos vías no pueden divergir; un endpoint por web; invalidación tras actualizar plugin, tema o núcleo; y un listado que distingue «sin datos» de «pendiente de refresco». En el frontend, el «Actualizar webs» abre un diálogo con barra de progreso y va web por web, con una pausa de dos segundos, un reintento y las webs caídas listadas como omitidas.

La prueba de humo cazó un detalle fino: al invalidar, la columna updated_at se refrescaba sola por el onupdate de SQLAlchemy y la edad del dato saltaba a cero. Una foto de dos horas habría pasado a decir «hace un momento» justo cuando el dato importa, que es justo lo contrario de lo que se había decidido. UPDATE explícito que conserva la edad, y un test que lo fija como invariante: invalidar no cambia cuándo se leyó el dato.

La actualización de núcleo que moría al instante

El reporte era seco: «cuando actualizo el core hace la petición pero parece que no hace la actualización». WordPress 7.1 cambió la firma de Core_Upgrader::upgrade() y pasó a exigir el objeto de actualización; el conector lo llamaba sin argumentos, como permitían las versiones anteriores. Resultado: un error de argumentos en 0,6 segundos y la versión sin cambiar. Como el manejador de fatales del plugin pierde la carrera contra el de WordPress, el detalle real estaba en el log de PHP del hosting, al que no se llega. Lo sacamos montando un WordPress desechable en fxserver, con la misma versión y el mismo PHP, y ahí el log cantó la causa.

El arreglo pasa el objeto de la oferta al upgrader, compatible con las versiones viejas donde era opcional; tras actualizar con éxito refresca el transient update_core —era el bug anunciado en el ADR-0001: el panel seguía viendo la actualización como pendiente hasta que pasara el wp-cron— y las rutas que tocan upgraders van envueltas en un guard que convierte cualquier error en un mensaje que el panel puede leer, en lugar de una página críptica. En el WordPress de debug, la llamada pasó de 500 instantáneo a actualizar de 7.1.3 a 7.1.4 de verdad.

El panel empuja el conector

Con el núcleo arreglado quedaba la otra mitad del problema: subir el conector nuevo significaba entrar en el wp-admin de cada web. Ahora el panel empuja su propio ZIP por una ruta firmada nueva y la web lo instala con Plugin_Upgrader y sin vaciar el directorio destino, para que el conector nunca se quede a medio instalar. Es empuje y no descarga por una razón simple: el conector no guarda la URL del panel, solo su secreto, así que no tiene a dónde pedir el ZIP.

Dos cosas que el entorno de pruebas no podía ver y un WordPress real destapó: el wrapper zip:// de PHP separa el archivo del ZIP con almohadilla, no con barra —con barra devuelve false y el ZIP parece corrupto—, y en un WordPress real faltaban los includes de wp-admin. El guard de rutas lo cantó limpio, con el error real en vez de la página genérica.

Verificado de punta a punta en el WordPress desechable: el conector pasó de 1.13.0 a 1.14.0 instalado por el panel, con el plugin vivo y la web respondiendo; luego se borró el stack. El entorno de pruebas cierra en 62 casos y el del API en 172 tests. Las nueve webs de producción necesitan una subida manual a la 1.13.0; a partir de ahí, se actualizan solas.