Playrix CI / CD: cómo construimos y probamos nuestros juegos

El equipo debe centrarse en crear juegos hermosos y exitosos, para todo lo demás hay CI.

¿Dónde aplicamos CI? ¿Qué enfoques y conceptos utilizamos? ¿Por qué compilar y probar compilaciones? La historia detallada sobre CI y cómo está organizada en Playrix dibujará un curso de conferencias. Debajo del corte: un breve apretón y algunos acentos.


Hola

Primero, un pequeño calentamiento: ¿qué es la integración continua? Si el equipo usa el repositorio y recopila compilaciones nocturnas, ¿ya es CI? ¿Cuál es la diferencia entre implementación continua y entrega? Es casi sin importancia. Detalles: para un círculo reducido de especialistas. Si desea involucrar a toda la empresa en algún proceso, invente un nombre simple y bueno para ello. En Playrix, llamamos a todos estos enfoques CI. Esta es una marca tan local y un logotipo genial:

Idea


CI no es un objetivo, es una herramienta. Debe haber un objetivo que desee alcanzar con la integración continua, un problema que debe resolverse. Sucede que los procesos de desarrollo y lanzamiento en un equipo se basan en el principio de "rezado y en producción". A veces está justificado, pero con poca frecuencia.

Formulamos nuestro objetivo de la siguiente manera: minimizar la probabilidad de problemas de integración, minimizar los recursos necesarios para corregir los errores encontrados, reducir el tiempo de los equipos de desarrollo de proyectos para apoyar y apoyar los procesos de CI.
CI es la automatización de procesos de compilación, pruebas de código y su entrega a diversos entornos, automatización de procesos de desarrollo rutinarios, integración mutua de servicios que todos usamos.

La idea está en un sistema que recolecta automáticamente todo, lo hace a menudo, prueba y entrega una compilación, y también apunta a un informe conveniente punto por punto si algo salió mal.

¿Dónde aplicamos CI?


  • Motor y utilidades,
  • nuestros juegos para todas las plataformas,
  • código de servidor
  • análisis, servicios de marketing, diversos automatismos, servicios de CI,
  • La infraestructura.

Eso está en todas partes o casi en todas partes.

Ensamblaje y prueba de contenedores, implementaciones automáticas en pruebas, puesta en escena y producción, actualizaciones de Rolling y Canary: todo esto lo tenemos y más aplicable para servicios y aplicaciones web. Hoy nos concentraremos en CI para juegos: crea compilaciones, pruébalas y entrégalas.

Paradigmas


Para lograr los objetivos establecidos anteriormente, debe resolver varios problemas. A continuación se muestra un plan que estamos siguiendo cuando automatizamos algún proceso de desarrollo, por ejemplo, ensamblar un cliente de juegos móviles. Es muy conveniente tener una lista de preguntas, respondiendo a las cuales, puede resolver cualquier problema que recaiga en el equipo de CI.

  • Documentación


Las instrucciones de montaje de la compilación son documentación, una descripción de nuestro proceso automatizado. A menudo, dicha documentación está en la cabeza de los programadores. Si un equipo tiene un súper especialista en compilaciones de edificios y sin él, nadie puede construir una compilación rápida y sin errores: es hora de cambiar algo, no habrá una presentación.

Bueno, si dicha documentación se enmarca en forma de script: ingresé una línea de comando y un conjunto de parámetros en una máquina con un entorno preparado; obtuve una compilación.

La mejor documentación del proceso es el código cat . Incluso si por alguna razón necesita repetir la operación manualmente, siempre puede hacerlo examinándolo.
  • Registro


El registro de compilación le permite decir siempre con certeza: quién, cuándo, de qué compromiso y con qué resultado se recopiló esta o aquella compilación. Ayer la construcción estaba a punto de hacerlo, pero hoy no. Buscamos en el registro, encontramos la primera compilación del disquete, vemos la lista de confirmaciones que llegaron allí: ganancias.

La revista es aún más útil cuando se trata, por ejemplo, del código del servidor. Si no hay información sobre quién actualizó el producto y cuándo, en el caso general no se sabe qué código está funcionando actualmente allí. Y a veces es muy importante, muy.

Puede mantener dicho diario en el libro mayor, preferiblemente en una tabla o wiki. La lista de compilaciones en el sistema CI no tiene precio.


  • Seguridad


Cuando se trata de construir una compilación y desplegarla en algún tipo de entorno, siempre surge la pregunta: ¿dónde almacenar las contraseñas de inicio de sesión / acceso? Por lo general, necesitan mucho: al repositorio para descargar los datos de origen, al almacenamiento de archivos para completar los recursos del juego, a HockeyApp para enviar personajes, al servidor para actualizar el código, etc.

Sucede que todos los accesos necesarios se almacenan en el repositorio. Existe la hipótesis de que esto no es muy bueno. A menudo puede ver el campo "ingresar contraseña", por ejemplo, en Jenkins, donde el autor de la compilación ingresa los caracteres ocultos.

Recordar todas las contraseñas de memoria es una buena habilidad. Nuestro propio servidor CI recibe los accesos necesarios según el ensamblaje. Por lo general, estos son tokens de corta duración que se generan al comienzo de la compilación y otorgan derechos mínimos exactamente donde implementamos algo o donde leemos algo.

La administración centralizada del ensamblaje y la implementación le permite resolver el problema de diferenciar los derechos de acceso a la infraestructura. ¿Por qué dar acceso a alguien al servidor si solo puede dar acceso al ensamblaje de la compilación correspondiente que realiza la operación necesaria en este servidor? Y dado que hay una compilación, entonces tenemos documentación y registro, bueno, ya entiendes.

  • Trazabilidad


Por lo general, quedan muchos rastros durante el tiempo de construcción. No, no así: durante la construcción, es necesario dejar tantos rastros como sea posible. En el repositorio, en el rastreador de tareas, en el sistema de distribución de compilación. En todas partes, donde sea que se encuentre con una compilación, debe haber rastros que lo lleven a completar la información al respecto.

Estos rastros no necesitan ser barridos, por el contrario, deben dejarse cuidadosamente y conservarse cuidadosamente. Además, le contaré más sobre esto, pero primero debemos recopilar nuestra compilación. Vamos

Ganchos de precompromiso


Nuevamente, la idea es un sistema que recolecta, prueba e informa todo. Pero, ¿por qué construir una compilación si no puedes construirla?

Todos los desarrolladores de nuestros juegos tienen ganchos de confirmación previa instalados, es decir Un conjunto de comprobaciones que se realizan cuando se intenta confirmar algo. Las comprobaciones solo se ejecutan para archivos modificados, pero implementamos un sistema de búsqueda de dependencia cruzada muy astuto para verificar también todo el contenido relacionado. Es decir Si el artista agregó una textura, los ganchos comprobarán que no se hayan olvidado de registrarla donde sea necesario y que nunca la hayan sellado.

Resulta que los ganchos capturan una parte significativa de pequeños errores. Ahorran los recursos del sistema de compilación y ayudan al desarrollador a solucionar rápidamente el problema: ve un mensaje que dice en detalle qué salió mal. Y no necesita cambiar entre tareas: literalmente solo hizo cambios y está en contexto. El tiempo de corrección de errores es mínimo.

Nos gustó tanto que incluso creamos un sistema que verifica si se hicieron ganchos para una confirmación que ingresó al repositorio. De lo contrario, el autor de dicha confirmación recibirá automáticamente una tarea pidiéndole que los configure e instrucciones detalladas sobre cómo hacerlo.

Los ganchos están estandarizados para todos los proyectos. El número de pruebas personalizadas es mínimo. Hay una personalización conveniente, incluso según el usuario que se esté ejecutando: esto es muy conveniente para probar pruebas.

Construir


Para ver el problema en la compilación lo antes posible, debe recopilar y probar estas compilaciones con la mayor frecuencia posible. Los clientes de nuestros juegos se reúnen para todas las plataformas, para cada commit, para todos los proyectos. Probablemente hay algunas excepciones, pero no muchas.

Por lo general, un cliente, especialmente uno móvil, tiene varias versiones diferentes: con y sin trampas, con firma diferente, etc. Para cada confirmación, recopilamos compilaciones "regulares", que los desarrolladores y evaluadores usan constantemente.

Hay compilaciones que se usan muy raramente, por ejemplo, la compilación ios de la tienda, solo una vez en un envío, es decir aproximadamente una vez al mes. Sin embargo, creemos que todas las compilaciones deben recopilarse regularmente. Si se produce un problema con este tipo de ensamblaje, en el lado del desarrollo o la infraestructura, el equipo del proyecto lo sabrá no el día de la entrega, sino mucho antes, y podrá responder y solucionar el problema con anticipación.

Como resultado, tenemos una regla simple: cualquier compilación se inicia al menos una vez al día. El equipo de desarrollo del proyecto se entera de la presencia de cualquier problema en cualquier plataforma en el peor de los casos a la mañana siguiente después de que este problema apareciera en el repositorio.

Tal frecuencia de ensambles y pruebas requiere un enfoque especial para optimizar su tiempo de ejecución.

  • Todas las compilaciones regulares de los clientes son incrementales.
  • Embalar atlas y preparar recursos también es incremental.
  • Los ensamblajes son granulares: algunos de los pasos están en configuraciones de compilación separadas; esto le permite ejecutarlos en paralelo, así como reutilizar resultados intermedios.

Esta es una captura de pantalla casi completa de la cadena de compilaciones y pruebas para WildScapes. Completo no se pudo hacer: es aproximadamente el doble de grande.


Pruebas Estáticas


Después del ensamblaje, se realiza una prueba estática: tomamos la carpeta con la compilación y realizamos un conjunto de comprobaciones de todo el contenido que está allí. El código también es contenido, por lo que su análisis estático (cppcheck + PVS-Studio) también está aquí.

En un habr había un material detallado sobre cómo hemos implementado las pruebas estáticas, lo recomiendo. Solo enfatizo que las pruebas estáticas después de la compilación y en los ganchos previos a la confirmación se ejecutan con el mismo código. Esto simplifica enormemente el soporte del sistema.

Pruebas de tiempo de ejecución


Si la compilación de pruebas estáticas se realizó correctamente, puede continuar e intentar ejecutar la compilación ensamblada. Estamos probando compilaciones en todas las plataformas excepto UWP, es decir. Windows, MacOs, iOS, Android. UWP - también lo será, pero un poco más tarde.

¿Por qué probar las compilaciones de escritorio si parecen ser necesarias solo en desarrollo? La respuesta a la pregunta es: es malo si un artista o un diseñador de niveles obtiene una compilación que se bloquea en el inicio por alguna razón ridícula. Por lo tanto, Smoke-Test, el conjunto mínimo de comprobaciones de capacidad de ejecución y juego básico, se realiza para todas las plataformas.

Todo lo que se escribió anteriormente sobre las compilaciones también es cierto para las pruebas en dispositivos, al menos una vez al día. Con pocas excepciones: hay pruebas muy largas que no tienen tiempo para completar en un día.

Las pruebas de humo se ejecutan para cada confirmación. La finalización exitosa de las comprobaciones básicas es un requisito previo para que la compilación ingrese al sistema de distribución. Por lo general, no tiene sentido dar acceso a alguien a una compilación que obviamente no funciona. Aquí puede objetar y presentar excepciones. Los proyectos tienen una solución alternativa para dar acceso a una compilación que no funciona, pero apenas la usan.

¿Qué otras pruebas hay?

  • Punto de referencia: verificamos el rendimiento en FPS y memoria en diversas situaciones y en todos los dispositivos.
  • Pruebas de unidades de combinación 3: cada elemento y cada mecánica se prueban tanto individualmente como en todas las combinaciones de interacción.
  • El paso de todo el juego de principio a fin.
  • Varias pruebas de regresión, por ejemplo, pruebas de localización, o que todas las ventanas de IU se abren correctamente, o que las escenas de fishdom en Fishdom se reproducen sin errores.
  • De todos modos , pero con AddressSanitizer .
  • Pruebas de compatibilidad para versiones de juegos: tome el archivo de guardar del usuario de versiones anteriores, ábralo en la nueva versión y asegúrese de que todo esté bien.
  • Diversas pruebas personalizadas relevantes para la mecánica de un proyecto en particular.

Para ejecutar las pruebas, utilizamos nuestro propio banco de pruebas de dispositivos iOS y Android. Esto nos permite lanzar de manera flexible las compilaciones que necesitamos en los dispositivos, interactuar con el dispositivo desde el código. Tenemos control total, un nivel de confiabilidad comprensible, sabemos qué problemas podemos enfrentar y cuánto tiempo llevará resolverlos. Ninguno de los servicios en la nube que proporcionan dispositivos de prueba ofrece tanta comodidad.

Gatos


Las pruebas enumeradas anteriormente se implementan dentro del código del proyecto. Esto permite en teoría hacer una prueba de cualquier complejidad, pero requiere esfuerzo y recursos del desarrollo del proyecto para implementar y apoyar estas pruebas. Estos recursos a menudo no están disponibles, y probar la regresión múltiple a mano es difícil y no necesario. Realmente quería que los probadores hicieran la automatización de pruebas. Y se nos ocurrió un marco para ellos: el Sistema de prueba de automatización continua, o CATS.

Cuál es la idea: permitir que los autores de guiones de prueba interactúen con la aplicación de juegos, sin preocuparse por cómo funciona todo. Escribimos scripts en python primitivo, accedemos a la aplicación a través de un conjunto de abstracciones. Por ejemplo: "Homescapes, ábrame una ventana de compras y compre tal o cual producto". Comprueba el resultado, bingo.

La implementación completa de los comandos de script está oculta detrás de un conjunto de abstracciones. La API que implementa la interacción con la aplicación le permite realizar cualquier acción de varias maneras:

  • envía una solicitud http al servidor, que está integrado en el motor del juego, con algún tipo de comando. Este comando es procesado por el código del juego. Por lo general, este es un tipo de trampa, puede ser arbitrariamente simple o complejo. Por ejemplo, "dame las coordenadas del centro del botón con el identificador especificado". O "pásame el juego de aquí al nivel con el número indicado".
  • Podemos abrir una ventana a través del truco o averiguar las coordenadas del botón por el cual se abre esta ventana, podemos emular haciendo clic en este botón, podemos hacer un clic virtual sobre él.
  • Finalmente, podemos hacer un clic "real" en las coordenadas especificadas como si se hiciera con un dedo en la pantalla.

El último método abre el espacio para la imaginación de los evaluadores que a menudo quieren probar construcciones de "combate", donde no hay trampas. Mantener tales escenarios es más difícil, pero una "construcción de combate" es una construcción de "combate".


Resultó ser muy conveniente trabajar con las coordenadas del centro del botón: las coordenadas a veces cambian, pero los identificadores de los botones son raros. Esto condujo a otra propiedad importante del sistema: la capacidad de escribir un script de prueba para todas las plataformas y todas las resoluciones de pantalla.

Entrega, informes y seguimientos


Con la entrega, todo resultó ser bastante simple: utilizamos un único almacenamiento compartido para artefactos de compilación y para el almacenamiento en el sistema de distribución. La "carga" de la compilación se reduce a invocar un par de solicitudes a la API del servicio de distribución de compilaciones, esencialmente registrarse. De esta manera, ahorramos un poco de tiempo en el bombeo de construcciones y dinero para su almacenamiento.

¿Recuerdas que hablaste de minimizar los recursos necesarios para corregir los errores encontrados en las compilaciones? Informes y pistas: casi sobre esto:

  • Informar sobre un problema encontrado es una tarea en Asana. Es fácil de controlar, asígnelo al desarrollador correcto, transfiéralo al equipo de CI si algo salió mal en la infraestructura.
  • Recopilamos compilaciones para cada commit. Conocemos al autor de este commit, por lo que solo él verá esta tarea. Por lo tanto, ahorramos tiempo para otros desarrolladores: no es necesario que se distraigan con problemas con los que no tienen nada que ver y que les ayuden a resolver lo que, muy probablemente, no podrán.
  • Si compila una compilación a partir de la próxima confirmación, lo más probable es que todavía esté rota. Habrá un comentario en la tarea: "La compilación todavía está rota", el autor de la nueva confirmación no verá la tarea y no perderá tiempo en el problema de otra persona.
  • Enviamos informes a Slack. Necesariamente, personalmente a quien "rompió" la construcción, y si el proyecto lo quiere, a un canal especial oa cualquier empleado de Playrix. Todo es lo más flexible posible.

Se necesitan rastreos para que en todas partes haya información completa sobre la compilación y los cambios de los que se recopiló. Para no buscar nada, para que todo esté a mano y no haya necesidad de pasar tiempo buscando detalles que a menudo son necesarios al investigar un problema.

  • El informe contiene un enlace a la compilación, al registro de compilación, el texto del error de compilación encontrado y los nombres de las pruebas invertidas. A menudo, un programador, después de haber recibido una tarea de informe, puede corregir inmediatamente el error: el nombre del archivo, la línea y el texto del error están en el informe.
  • El mensaje en Slack contiene lo mismo + un enlace a la tarea en Asana.
  • En Teamcity, un enlace a una tarea. El ingeniero de compilación puede entrar inmediatamente en la tarea, con un solo clic, no es necesario buscar nada.
  • En github, estado con un enlace a la compilación, en el comentario a la confirmación, un enlace a la tarea para la que se realizó esta compilación. En la tarea: un comentario con un enlace a la confirmación.
  • En el servicio de distribución de compilación: enlace a la compilación, enlace a la confirmación.

No hay nada que recordar, pero entendiste la idea: enlaces a todo, en todas partes. Esto acelera enormemente el estudio de cualquier situación incomprensible.

Granja


Recopilamos y probamos compilaciones para todas las plataformas. Para esto necesitamos muchos agentes diferentes. Rastrearlos y mantenerlos manualmente es largo y difícil. Todos los agentes se preparan automáticamente. Utilizamos Packer y Ansible.

Todos los registros de todos los agentes, Teamcity, todos los servicios que existen, los guardamos (en nuestro caso, en ELK). Todos los servicios, al procesar una compilación, agregan el número de esta compilación a cada línea de registros. Podemos ver en una sola solicitud el ciclo de vida completo de la compilación desde su aparición en la cola hasta el final del envío de todos los informes.

Hemos implementado nuestro propio mecanismo de optimización de colas. El de Teamcity no funciona muy bien en nuestros números. Hablando de números:

  • Recopilamos alrededor de 5,000 construcciones todos los días. Esto es aproximadamente 500 horas máquina de trabajo.
  • La construcción número tres millones fue hace un mes.
  • Tenemos más de 50 servidores de compilación en 10 ubicaciones diferentes.
  • Más de 40 dispositivos móviles en un banco de pruebas.
  • Exactamente 1 servidor Teamcity.


CI como servicio


Playrix CI es un servicio. Hay muchos proyectos, muchas ideas también.

Optimizamos el tiempo desde que la compilación se pone en la cola hasta el final de su ejecución, porque eso es exactamente lo que el usuario del servicio, el desarrollador, considera el "tiempo de compilación". Esto nos permite buscar y encontrar un equilibrio entre el tiempo de compilación y el tiempo que pasó en la cola. Parece lógico que con el crecimiento de la empresa y la cantidad de proyectos, la granja de construcción que recopila estos proyectos también crecerá. Pero gracias a las optimizaciones, la tasa de crecimiento de la granja está muy por detrás de la tasa de crecimiento de la compañía.

Cualquier optimización comienza con el monitoreo, con una recopilación metódica de estadísticas. Recopilamos muchas estadísticas y sabemos absolutamente todo acerca de nuestras compilaciones. Pero además del volumen de la granja de compilación, también hay un equipo que apoya el sistema CI y lo hace para que nadie tenga que pensar de dónde provienen las compilaciones.

La optimización de procesos en este equipo también es un proceso interesante y entretenido.Por ejemplo, escribimos pruebas para establecer configuraciones de compilaciones, porque hay muchas de estas configuraciones, sin pruebas similares no es fácil encontrar todos los lugares que necesitan ser editados. Para casi cualquier cambio, primero escribimos una prueba y luego los hacemos, es decir, tenemos TDD. Hay muchos procesos relacionados con el deber, la gestión de incidentes, la programación del flujo de tareas entrantes.

Los desarrolladores deben centrarse en crear juegos geniales y exitosos sin preocuparse de dónde provienen las compilaciones. Para esto, Playrix tiene un CI. Debe haber un objetivo que desee alcanzar con la integración continua, un problema que debe resolverse. Es importante no encontrar un problema, es decir, encontrarlo. Y cuando la encuentres, recuerda nuestra experiencia y hazlo mejor. Y recuerda:

CI nunca duerme
¡Nos vemos!

Source: https://habr.com/ru/post/479966/


All Articles