Volver al Centro de conocimiento
Huella digital

Chrome for Testing: fijar una build, no una huella

El endpoint de disponibilidad publica cuatro canales y tres artefactos por plataforma en una versión. Fijar esa versión deja fuera fuentes, GPU y reloj.

BotBrowser Team

Documentación

Quieres la documentación estructurada de Huella digital?

Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.

Una build fijada es la victoria de reproducibilidad más barata disponible, y es más pequeña de lo que parece. BotBrowser proporciona un perfil declarado que mantiene coherentes entre sí las superficies que una página puede leer en cada lanzamiento, y no decide qué binario está instalado. La lectura directa de los endpoints de Chrome for Testing muestra cuatro canales con sus versiones, seis plataformas que publican un navegador, un driver y un headless shell en una misma versión, y después la lista de valores que siguen moviéndose cuando la versión ya está fijada.

TL;DR

  • El endpoint de disponibilidad publica cuatro canales, Stable, Beta, Dev y Canary, cada uno con una versión y una revisión.
  • En la fecha de lectura, Stable era 157.0.8103.50 con revisión 1717349 y Canary era 159.0.8154.2 con revisión 1739869.
  • Cada plataforma publica tres artefactos en una versión: el navegador, chromedriver y chrome-headless-shell.
  • Hay seis plataformas, linux-arm64, linux64, mac-arm64, mac-x64, win32 y win64, y los tres artefactos existen para cada una.
  • Cada URL publicada para una versión lleva esa misma cadena de versión, y eso es lo que hace significativa una fijación.
  • La lista de versiones conocidas como buenas contenía 2626 versiones, de la mayor 113 a la 159, en el momento de la lectura.
  • Fijar la build corrige el renderizador y el driver a la vez, y no corrige las fuentes, los drivers de GPU, las bibliotecas del sistema ni el reloj.
  • Un perfil describe lo que informa el entorno; una build fijada describe lo que se instaló. Hacen falta ambos y ninguno sustituye al otro.

Contenido

Tres paneles. El primero enumera los cuatro canales con sus versiones y revisiones. El segundo muestra seis plataformas, cada una con un navegador, un driver y un headless shell en una misma cadena de versión. El tercero separa lo que corrige una build fijada de lo que sigue moviéndose: fuentes, drivers de GPU, bibliotecas del sistema y reloj.

Qué publican los endpoints

El primer dato útil es que la disponibilidad se publica como datos y no como una página de descargas, y eso es lo que hace posible una fijación automatizada. Dos endpoints cubren casi todo lo que necesita un pipeline: uno informa la última build conocida como buena de cada canal, y otro informa todas las versiones conocidas como buenas.

last_known_good_channels: Stable, Beta, Dev, Canary
  Stable: version=157.0.8103.50 revision=1717349
  Beta:   version=158.0.8127.18 revision=1727472
  Dev:    version=159.0.8144.0 revision=1735750
  Canary: version=159.0.8154.2 revision=1739869

Dos campos importan y responden preguntas distintas. La versión es la cadena que reconoce un lector, y la revisión es la posición de commit subyacente en el repositorio de Chromium. Una fijación escrita contra la versión es legible; una fijación escrita contra la revisión no deja ambigüedad aunque un canal vuelva a publicar una build. Registrar ambos campos es la opción barata, y registrar solo la versión es el error habitual.

La estructura de canales también importa para un pipeline que quiere estabilidad sin quedarse obsoleto. Stable se mueve despacio y es la opción correcta por defecto para una puerta de release. Beta, Dev y Canary se mueven más rápido y sirven para detectar un cambio antes de que llegue a Stable, que es lo contrario de lo que pide un requisito de reproducibilidad. Un pipeline que sigue Canary para lograr determinismo ha elegido el canal más móvil para el trabajo que necesita el más lento.

known_good_count: 2626
oldest: 113.0.5672.0   newest: 159.0.8154.2
majors_present (last six): 154, 155, 156, 157, 158, 159

El tamaño de esa lista es el argumento práctico para leerla por programa. Dos mil seiscientas versiones no es una cifra que una persona revise a ojo, y una fijación editada a mano se desvía en el momento en que alguien copia una versión de una entrada publicada en lugar del endpoint.

Tres artefactos, una versión

El segundo dato es el que rompe la mayoría de los pipelines que fijan solo el navegador. Cada versión publica tres artefactos separados, y una instalación de automatización que funcione necesita que los tres coincidan.

chrome_platforms: linux-arm64, linux64, mac-arm64, mac-x64, win32, win64
mac-arm64: chrome=true driver=true shell=true
linux64:   chrome=true driver=true shell=true
win64:     chrome=true driver=true shell=true
distinct_versions_in_urls: 157.0.8103.50
all_artefacts_agree: true

Esa última línea es la que sostiene todo. Cada URL publicada para la versión lleva la misma cadena de versión, así que fijar un valor fija el conjunto. Cuando los tres no coinciden, el fallo no es aleatorio: un driver más nuevo que su navegador se niega a conectarse, y un headless shell de otra build renderiza distinto del navegador que debería representar.

El headless shell es el artefacto que los equipos olvidan, y es el que cambia los resultados en silencio. Un test que se ejecuta contra chrome-headless-shell no se ejecuta contra el binario del navegador, así que fijar el navegador y dejar el shell libre produce un pipeline donde el renderizador cambia sin que se mueva el número de versión. Comprobar que los tres informan la misma versión es una aserción de una línea, y detecta el caso que una fijación solo del navegador no puede.

pin = "157.0.8103.50"
assert browser_version == pin
assert driver_version  == pin
assert shell_version   == pin

La misma estructura explica un fallo que parece intermitencia. Una imagen de contenedor que instala el navegador desde una fuente y el driver desde un gestor de paquetes puede acabar con dos versiones de la misma mayor, que funcionan casi siempre y fallan de forma intermitente en la única capacidad que cambió. La aserción anterior convierte ese fallo intermitente en un error de arranque, que es el resultado por el que vale la pena pagar.

Qué corrige una fijación y qué no

El tercer dato es el que decide si una fijación basta, y la respuesta honesta es que corrige menos de lo que un lector espera. Una build fijada corrige el renderizador y el driver a la vez, porque son los artefactos que nombra.

pinned by the build:  renderer version, driver version, headless shell version
not pinned:           fonts, GPU driver, system libraries, time zone, clock

Las fuentes son el residuo más común. Un contenedor con un navegador fijado y sin fuente CJK renderiza una página china como cajas, y el resultado en píxeles difiere del de una máquina de desarrollo con la misma versión de navegador. Nada en el conjunto de artefactos corrige eso, porque las fuentes vienen de la imagen del sistema operativo y no del paquete del navegador.

La pila de GPU es el segundo. Un navegador fijado en una máquina con renderizado por software produce una salida de canvas y WebGL distinta de la del mismo navegador en una máquina con un adaptador real, y ambos son la misma versión. Esa diferencia es medible y no es un defecto de la fijación; es una variable que la fijación nunca cubrió.

El reloj y la zona horaria son el tercero, y son los más fáciles de pasar por alto porque no forman parte del navegador en absoluto. Un test que lee una fecha formateada o una cadena dependiente del idioma puede pasar y fallar en la misma build según la configuración del host, y un informe que registra solo la versión del navegador no puede explicar la diferencia.

La lectura práctica es que una fijación es un primer paso necesario y un mal punto de parada, la misma conclusión a la que llega el modo determinista para automatización de navegador desde el lado de la planificación. Elimina la mayor fuente de deriva, que es el renderizador cambiando bajo un test fijo, y deja tres fuentes menores que un registro tiene que nombrar por separado. Un pipeline que registra la build y nada más puede responder «¿cambió el navegador?» y no puede responder «¿cambió el entorno?».

Una cuarta observación corresponde aquí porque es la que convierte una fijación en una rutina operativa. Los canales se mueven a su propio ritmo, así que una fijación que nunca se revisa se convierte en la decisión de dejar de recibir correcciones. La rutina que mantiene honesta una fijación tiene tres pasos y ninguno es automático: leer el endpoint, comparar la versión Stable publicada con la fijada y decidir si la diferencia es una corrección de seguridad que hay que tomar o un cambio de funcionalidad que puede esperar a la siguiente ventana planificada.

pinned:   157.0.8103.50
published:157.0.8103.50
delta:    none
action:   none

Esa comparación es barata y es el paso que los equipos se saltan. Un pipeline que fija y nunca compara acabará ejecutando una build con un defecto conocido mientras el panel muestra un número de versión saludable, porque la versión está fijada y por tanto parece intencionada. Registrar el valor publicado junto al valor fijado hace visible la deriva sin exigir que nadie recuerde cuándo se escribió la fijación.

Hay una segunda razón para mantener la comparación. Un salto de versión mayor cambia el renderizador de formas que un salto de parche no cambia, y una fijación que cruza una mayor es una decisión distinta de una fijación que toma un parche. Tratar ambas como «una actualización» junta un cambio de bajo riesgo y uno de alto riesgo en una sola acción, y la revisión que sigue es entonces demasiado pesada para el parche o demasiado ligera para la mayor.

La propia cadena de versión lleva esa información y un lector puede usarla sin un changelog. Los cuatro componentes siguen los campos que Chromium registra en chrome/VERSION: MAJOR.MINOR.BUILD.PATCH, así que 157.0.8103.50 se lee como mayor 157, menor 0, build 8103 y parche 50. Un cambio en el primer componente es el que merece una base de referencia completa nueva; un cambio en el último suele ser el menos disruptivo. Leer la cadena en lugar de tratarla como un token opaco es lo que permite ese juicio antes de descargar nada.

Nada de esto requiere una herramienta nueva. El endpoint es JSON, la comparación es una igualdad de cadenas y la aserción son tres comprobaciones al arrancar. Lo que requiere es la decisión de registrar la build junto al perfil, porque un informe que nombra uno y no el otro no puede responder la pregunta que un lector hará de verdad.

Conclusión práctica

Lee el endpoint de disponibilidad en lugar de una entrada publicada, registra la versión y la revisión juntas y afirma al arrancar que el navegador, chromedriver y chrome-headless-shell informan el valor fijado. Sigue Stable para una puerta de release y reserva los canales rápidos para detectar cambios, porque un requisito de determinismo y un requisito de frescura tiran en direcciones opuestas. Después registra lo que la fijación no cubre, que es el conjunto de fuentes, la pila de GPU, las bibliotecas del sistema y el reloj. Para el contrato de release que esto implementa, lee fijación y actualización de versiones del navegador; para entender por qué dos máquinas con la misma build aún pueden discrepar, lee divergencia de huellas entre nodos de Selenium Grid.

Un perfil declarado y una build fijada responden dos preguntas distintas. El perfil dice lo que el entorno informa a una página; la build dice lo que se instaló. Una instalación que tiene uno y no el otro puede ser coherente en una dimensión y estar derivando en la otra, y un informe que nombra ambos es el único que puede comprobarse después.

Un último grupo de observaciones se refiere a para qué sirven los tres artefactos, porque un lector que los trata como intercambiables fijará el equivocado. El binario del navegador es el renderizador y todo lo que se sigue de él: maquetación, fuentes que incluye, motor de JavaScript y las superficies que una página puede leer. El driver es un puente de protocolo, y su versión tiene que coincidir porque el protocolo que habla se genera a partir de la propia definición de protocolo del navegador.

browser   -> renderer, layout, JS engine, page-readable surfaces
driver    -> protocol bridge; must match the browser it drives
shell     -> headless renderer used instead of the browser binary

Esa fila central es la razón por la que un desajuste de driver falla de forma ruidosa y no sutil. Un driver compilado contra otra generación de protocolo rechazará una sesión o fallará en el primer comando que cambió de forma, y el error nombra un comando y no una versión. Un lector que ve un error de protocolo y una cadena de versión que parece coincidir normalmente está mirando un driver instalado por un gestor de paquetes y no desde el conjunto de artefactos.

La tercera fila es la que produce diferencias silenciosas. El headless shell es un binario aparte, así que un test que se ejecuta contra él no se ejecuta contra el navegador en absoluto, incluso cuando las cadenas de versión coinciden. Los dos renderizan la misma página de forma ligeramente distinta justo en los lugares que nota una comparación visual: posiciones de texto subpíxel, orden de sustitución de fuentes y la forma en que se decodifica una imagen de carga diferida. Un pipeline que fija el navegador y deja libre el shell ha fijado el artefacto que no usa y ha dejado sin fijar el que sí usa.

Hay una consecuencia más para un informe de huellas. Un valor leído de una página depende del renderizador, así que un informe que nombra la versión del navegador y no la del shell ha nombrado el artefacto equivocado siempre que la ejecución usó el shell. En la práctica no es una distinción sutil: es la diferencia entre un informe que se puede reproducir y uno que no.

La regla práctica que se sigue es corta. Fija los tres juntos, afirma los tres al arrancar y registra cuál de los tres se ejecutó de verdad. Un pipeline que hace esas tres cosas puede responder la pregunta que hará un lector posterior, que no es «qué versión se fijó» sino «qué binario produjo este resultado».

Una nota sobre los dos endpoints, porque sus nombres se confunden con facilidad y la diferencia decide cómo se escribe una fijación. Un endpoint responde «cuál es la build actual de este canal» y cambia cada vez que un canal se vuelve a publicar. El otro responde «qué versiones existen» y solo crece. Una fijación escrita desde el primer endpoint es una instantánea de un valor móvil; una escrita desde el segundo es una referencia a una entrada inmutable, que es lo que una build reproducible necesita de verdad.

last-known-good-versions.json -> current per channel; changes on republish
known-good-versions.json      -> every published version; only grows
<version>.json                -> the artefact list for one version; immutable

La tercera línea, el documento 157.0.8103.50.json, es la que un pipeline debería leer al instalar. Está indexado por la cadena de versión, así que una fijación se resuelve en un documento que nombra cada artefacto para cada plataforma, y la resolución no depende de qué canal está sano en ese momento. Un pipeline que lee el primer endpoint al instalar instalará otra build la semana siguiente sin ningún cambio en su configuración, que es lo contrario de para lo que sirve una fijación.

La distinción también explica una clase de informe de error confuso. Una suite que pasó ayer y falla hoy sin cambio de código normalmente está leyendo el endpoint móvil en algún punto de su preparación, y la solución no es perseguir el fallo sino hacer la lectura inmutable. Es un cambio de una línea y elimina toda una categoría de fallo intermitente del registro.

Para un equipo que también necesita saber cuándo moverse, el primer endpoint sigue siendo el correcto para leer, solo que en otro momento. Pertenece a una comprobación programada que informa el delta, no a la ruta de instalación que tiene que ser reproducible. Separar las dos lecturas por propósito es lo que permite que un pipeline sea reproducible y actual a la vez, lo que parece contradictorio solo hasta que las dos se separan.

Una consecuencia más se sigue de la división de artefactos, y se refiere a lo que un registro tiene que contener antes de poder auditarse. Un identificador de build responde qué renderizador produjo un resultado. No responde qué fuentes estaban disponibles, qué adaptador gráfico estaba presente o qué reloj leyó el proceso. Esas tres son las variables que convierten una build idéntica en un resultado distinto, y ninguna está en el conjunto de artefactos por diseño.

record: build, fonts present, graphics adapter, time zone
missing any one -> the result is reproducible only by luck

La forma más barata de mantener ese registro es escribirlo al arrancar en lugar de reconstruirlo después. Una línea de arranque que nombra la build, la longitud de la lista de fuentes, la cadena del adaptador y la zona, como America/Toronto, cuesta unos cientos de bytes y responde la pregunta que un lector hará cuando un resultado cambie. Reconstruirlo después significa adivinar sobre una imagen que puede haberse reconstruido desde entonces, que es la versión cara del mismo trabajo.

El caso de las fuentes merece una frase propia porque es el que produce una respuesta equivocada en lugar de un fallo. Una fuente CJK ausente no lanza un error; sustituye por una fuente alternativa, la página se sigue renderizando, el texto extraído sigue siendo correcto y la captura difiere. Un pipeline que compara capturas ve una diferencia que no puede explicar, y un pipeline que compara texto extraído no ve nada. Registrar el recuento de fuentes convierte ambos casos en una condición observable.

Esa es la forma del tema completo. Una fijación elimina la mayor variable y deja un conjunto pequeño de otras, y el valor del trabajo está en nombrar el resto con precisión en lugar de fingir que la fijación lo cubrió todo. Un lector que registra cuatro campos puede explicar un cambio; un lector que registra uno solo puede informar que algo se movió.

Un último apunte operativo cierra el círculo sobre para qué sirve anclar. La razón para fijar una compilación no es que el cambio sea malo, sino que un cambio no anunciado vuelve un fallo inatribuible. Una prueba que falla tras una actualización se explica por la actualización, y una prueba que falla sin ningún cambio registrado obliga a una investigación que empieza de cero. Anclar compra atribución.

unpinned + failure -> investigate the browser, the fonts, the GPU, the code
pinned   + failure -> compare against the last run on the same build

Fuentes

#Chrome For Testing#Chromedriver#Fijación De Versión#Automatización#Reproducibilidad

Este artículo forma parte de la guía: ¿Qué es la huella digital del navegador?

Ejecuta la misma comprobación en BotBrowser

Carga un perfil, mantén dentro del navegador las señales que acabas de leer y confirma el resultado en tu propia máquina antes del despliegue.