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 del núcleo vuelve a funcionar con WordPress 7.1 y el panel empuja el conector a los sitios web sin entrar en ningún wp-admin.
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 se le pide y, además, ya empuja las actualizaciones del conector a los sitios web sin entrar en ningún wp-admin.
Un botón que pintaba acción
El controlador del «Actualizar» era, literalmente, una lectura de la base de datos del propio panel. No realizaba ninguna llamada a WordPress. Debajo había tres capas encadenadas: el proceso en segundo plano solo refrescaba los sitios web cuya caché tenía más de dos horas; actualizar un complemento 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 consultaba siempre datos en vivo. El listado y el espacio de trabajo contaban dos verdades distintas del mismo sitio web.
Lo nuevo es: un servicio único de refresco que utilizan tanto el proceso en segundo plano como el botón, de modo que las dos vías no puedan divergir; un punto de conexión por sitio web; la invalidación después de actualizar un complemento, un tema o el núcleo; y un listado que distingue entre «sin datos» y «pendiente de refresco». En la interfaz, «Actualizar sitios web» abre un diálogo con una barra de progreso y avanza sitio web por sitio web, con una pausa de dos segundos, un reintento y los sitios web caídos enumerados como omitidos.
La prueba de humo detectó un detalle sutil: al invalidar, la columna updated_at se refrescaba sola por el onupdate de SQLAlchemy y la antigüedad del dato saltaba a cero. Una instantánea de dos horas habría pasado a decir «hace un momento» justo cuando el dato importaba, que era justo lo contrario de lo que se había decidido. Se añadió una operación UPDATE explícita que conserva la antigüedad y una prueba que la fija como invariante: invalidar no cambia cuándo se leyó el dato.
La actualización del núcleo que moría al instante
El informe era escueto: «cuando actualizo el núcleo, 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. El resultado fue un error de argumentos en 0,6 segundos y la versión sin cambiar. Como el manejador de errores fatales del complemento pierde la carrera contra el de WordPress, el detalle real estaba en el registro de PHP del alojamiento, al que no se llegaba. Lo descubrimos montando un WordPress desechable en fxserver, con la misma versión y el mismo PHP, y allí el registro mostró la causa.
El arreglo pasa el objeto de la oferta al actualizador, compatible con las versiones antiguas, donde era opcional; tras actualizar con éxito, refresca el dato temporal update_core —era el error anunciado en ADR-0001: el panel seguía viendo la actualización como pendiente hasta que pasara el wp-cron— y las rutas que tocan los actualizadores van envueltas en una protección que convierte cualquier error en un mensaje que el panel puede leer, en lugar de una página críptica. En el WordPress de depuración, la llamada pasó de un 500 instantáneo a actualizar de verdad de 7.1.3 a 7.1.4.
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 sitio web. Ahora el panel empuja su propio archivo ZIP por una nueva ruta firmada y el sitio web lo instala con Plugin_Upgrader, sin vaciar el directorio de destino, para que el conector nunca quede a medio instalar. Es un empuje y no una descarga por una razón sencilla: el conector no guarda la URL del panel, solo su secreto, así que no tiene dónde pedir el archivo ZIP.
Dos cosas que el entorno de pruebas no podía ver y que un WordPress real destapó: la envoltura zip:// de PHP separa la ruta del archivo dentro del ZIP con una almohadilla, no con una barra —con barra devuelve el valor booleano falso y el ZIP parece corrupto—, y en un WordPress real faltaban las inclusiones de wp-admin. La protección de rutas dejó ver el error real con claridad, en lugar 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 complemento activo y el sitio web respondiendo; luego se eliminó la pila de servicios. El entorno de pruebas cerró con 62 casos y el de la interfaz de programación de aplicaciones con 172 pruebas. Los nueve sitios web de producción necesitan una subida manual a la 1.13.0; a partir de ahí, se actualizan solos.