Evolución de la CI en el equipo de desarrollo móvil

Hoy en día, la mayoría de los productos de software se desarrollan en equipos. Las condiciones para el desarrollo exitoso de un equipo se pueden representar en forma de un diagrama simple.

Evolución de la CI en el equipo de desarrollo móvil

Una vez que haya escrito su código, debe asegurarse de que:

  1. Funciona
  2. No rompe nada, incluido el código que escribieron sus colegas.

Si se cumplen ambas condiciones, entonces estás en el camino del éxito. Para comprobar fácilmente estas condiciones y no desviarnos del camino rentable, se nos ocurrió la Integración Continua.

CI es un flujo de trabajo en el que integra su código en el código general del producto con la mayor frecuencia posible. Y no sólo integras, sino que también compruebas constantemente que todo funciona. Dado que es necesario comprobar mucho y con frecuencia, vale la pena pensar en la automatización. Puedes comprobar todo manualmente, pero no deberías hacerlo, y he aquí por qué.

  • querida gente. Una hora de trabajo de cualquier programador es más cara que una hora de trabajo de cualquier servidor.
  • La gente comete errores. Por lo tanto, pueden surgir situaciones en las que se ejecutaron pruebas en la rama incorrecta o se compiló una confirmación incorrecta para los evaluadores.
  • la gente es vaga. De vez en cuando, cuando termino una tarea, me surge el pensamiento: “¿Qué hay que comprobar? Escribí dos líneas: ¡todo funciona! Creo que algunos de ustedes a veces también tienen esos pensamientos. Pero siempre debes comprobarlo.

Cómo se implementó y desarrolló la integración continua en el equipo de desarrollo móvil de Avito, cómo pasaron de 0 a 450 construcciones por día y cómo las máquinas de construcción ensamblan 200 horas al día, dice Nikolai Nesterov (nnesterov) participa en todos los cambios evolutivos de la aplicación CI/CD de Android.

La historia se basa en el ejemplo de un comando de Android, pero la mayoría de los enfoques también son aplicables en iOS.


Érase una vez una persona que trabajaba en el equipo de Avito Android. Por definición, no necesitaba nada de la Integración Continua: no había nadie con quien integrarse.

Pero la aplicación creció, aparecieron cada vez más tareas nuevas y el equipo creció en consecuencia. En algún momento, llegará el momento de establecer más formalmente un proceso de integración de código. Se decidió utilizar Git flow.

Evolución de la CI en el equipo de desarrollo móvil

El concepto de flujo de Git es bien conocido: un proyecto tiene una rama de desarrollo común, y para cada característica nueva, los desarrolladores cortan una rama separada, se comprometen con ella, la impulsan y, cuando quieren fusionar su código en la rama de desarrollo, abren una. solicitud de extracción. Para compartir conocimientos y discutir enfoques, introdujimos la revisión de código, es decir, los colegas deben verificar y confirmar el código de los demás.

Cheques

Ver el código con los ojos es genial, pero no suficiente. Por ello se están introduciendo controles automáticos.

  • En primer lugar, comprobamos Asamblea ARCA.
  • Muchos pruebas conjuntas.
  • Consideramos la cobertura del código., ya que estamos realizando pruebas.

Para comprender cómo se deben ejecutar estas comprobaciones, veamos el proceso de desarrollo en Avito.

Se puede representar esquemáticamente así:

  • Un desarrollador escribe código en su computadora portátil. Puede ejecutar comprobaciones de integración aquí mismo, ya sea con un enlace de confirmación o simplemente ejecutar comprobaciones en segundo plano.
  • Una vez que el desarrollador ha enviado el código, abre una solicitud de extracción. Para que su código se incluya en la rama de desarrollo, es necesario realizar una revisión del código y recopilar la cantidad requerida de confirmaciones. Puede habilitar comprobaciones y compilaciones aquí: hasta que todas las compilaciones sean exitosas, la solicitud de extracción no se puede fusionar.
  • Después de fusionar la solicitud de extracción y incluir el código en desarrollo, puede elegir un momento conveniente: por ejemplo, por la noche, cuando todos los servidores están libres, y ejecutar tantas comprobaciones como desee.

A nadie le gustaba realizar escaneos en su computadora portátil. Cuando un desarrollador ha terminado una función, quiere impulsarla rápidamente y abrir una solicitud de extracción. Si en este momento se inician algunas comprobaciones prolongadas, esto no sólo no es muy agradable, sino que también ralentiza el desarrollo: mientras el portátil está comprobando algo, es imposible trabajar normalmente en él.

Nos gustó mucho realizar comprobaciones por la noche, porque hay mucho tiempo y servidores que puedes recorrer. Pero, desafortunadamente, cuando el código de característica comienza a desarrollarse, el desarrollador tiene mucha menos motivación para corregir los errores que encontró CI. De vez en cuando, cuando miraba todos los errores encontrados en el informe de la mañana, me sorprendía pensando que los solucionaría algún día más tarde, porque ahora hay una nueva tarea interesante en Jira que solo quiero comenzar a hacer.

Si las comprobaciones bloquean una solicitud de extracción, entonces hay suficiente motivación, porque hasta que las compilaciones se pongan verdes, el código no se desarrollará, lo que significa que la tarea no se completará.

Como resultado, elegimos la siguiente estrategia: ejecutamos el máximo conjunto posible de comprobaciones por la noche y lanzamos las más críticas y, lo más importante, las más rápidas en una solicitud de extracción. Pero no nos detenemos ahí: en paralelo, optimizamos la velocidad de las comprobaciones para transferirlas del modo nocturno a las comprobaciones de solicitud de extracción.

En ese momento, todas nuestras compilaciones se completaron con bastante rapidez, por lo que simplemente incluimos la compilación de ARK, las pruebas de Junit y los cálculos de cobertura de código como bloqueador de la solicitud de extracción. Lo activamos, pensamos en ello y abandonamos la cobertura del código porque pensamos que no la necesitábamos.

Nos tomó dos días configurar completamente el CI básico (en adelante, el tiempo estimado es aproximado, necesario para la escala).

Después de eso, comenzamos a pensar más: ¿estamos verificando correctamente? ¿Estamos ejecutando correctamente las compilaciones de las solicitudes de extracción?

Comenzamos la compilación en la última confirmación de la rama desde la cual se abrió la solicitud de extracción. Pero las pruebas de esta confirmación sólo pueden mostrar que el código que escribió el desarrollador funciona. Pero no prueban que no haya roto nada. De hecho, debe verificar el estado de la rama de desarrollo después de que se fusiona una característica en ella.

Evolución de la CI en el equipo de desarrollo móvil

Para hacer esto, escribimos un script bash simple. premerge.sh:

#!/usr/bin/env bash

set -e

git fetch origin develop

git merge origin/develop

Aquí, todos los cambios más recientes del desarrollo simplemente se extraen y se fusionan en la rama actual. Agregamos el script premerge.sh como primer paso en todas las compilaciones y comenzamos a verificar exactamente lo que queremos, es decir integración.

Nos llevó tres días localizar el problema, encontrar una solución y escribir este script.

La aplicación se desarrolló, aparecieron cada vez más tareas, el equipo creció y, en ocasiones, premerge.sh empezó a decepcionarnos. Develop tuvo cambios contradictorios que rompieron la compilación.

Un ejemplo de cómo sucede esto:

Evolución de la CI en el equipo de desarrollo móvil

Dos desarrolladores comienzan a trabajar simultáneamente en las características A y B. El desarrollador de la característica A descubre una característica no utilizada en el proyecto. answer() y, como buen boy scout, se lo quita. Al mismo tiempo, el desarrollador de la función B agrega una nueva llamada a esta función en su rama.

Los desarrolladores terminan su trabajo y abren una solicitud de extracción al mismo tiempo. Se lanzan las compilaciones, premerge.sh verifica ambas solicitudes de extracción con respecto al último estado de desarrollo; todas las comprobaciones están en verde. Después de eso, la solicitud de extracción de la característica A se fusiona y la solicitud de extracción de la característica B se fusiona... ¡Boom! El desarrollo se interrumpe porque el código de desarrollo contiene una llamada a una función inexistente.

Evolución de la CI en el equipo de desarrollo móvil

Cuando no se va a desarrollar, es desastre local. Todo el equipo no puede recopilar nada y enviarlo para su prueba.

Dio la casualidad de que trabajaba con mayor frecuencia en tareas de infraestructura: análisis, redes, bases de datos. Es decir, fui yo quien escribió esas funciones y clases que usan otros desarrolladores. Debido a esto, me encontré en situaciones similares muy a menudo. Incluso tuve este cuadro colgado por un tiempo.

Evolución de la CI en el equipo de desarrollo móvil

Como esto no nos convenía, comenzamos a explorar opciones sobre cómo prevenirlo.

Cómo no romper el desarrollo

La primera opción: Reconstruya todas las solicitudes de extracción al actualizar el desarrollo. Si, en nuestro ejemplo, la solicitud de extracción con la característica A es la primera que se incluye en el desarrollo, la solicitud de extracción de la característica B se reconstruirá y, en consecuencia, las comprobaciones fallarán debido a un error de compilación.

Para comprender cuánto tiempo llevará esto, considere un ejemplo con dos RP. Abrimos dos relaciones públicas: dos compilaciones, dos ejecuciones de comprobaciones. Después de que el primer RP se fusiona con el desarrollo, es necesario reconstruir el segundo. En total, dos RP requieren tres series de comprobaciones: 2 + 1 = 3.

En principio está bien. Pero miramos las estadísticas y la situación típica en nuestro equipo era 10 RP abiertos, y luego el número de controles es la suma de la progresión: 10 + 9 +... + 1 = 55. Es decir, aceptar 10 PR, necesitas reconstruir 55 veces. Y esto es en una situación ideal, cuando todas las comprobaciones pasan la primera vez, cuando nadie abre una solicitud de extracción adicional mientras se procesan estas docenas.

Imagínese como un desarrollador que necesita ser el primero en hacer clic en el botón "fusionar", porque si un vecino hace esto, tendrá que esperar hasta que todas las compilaciones se realicen nuevamente... No, eso no funcionará. , ralentizará seriamente el desarrollo.

Segunda forma posible: recopilar solicitudes de extracción después de la revisión del código. Es decir, abre una solicitud de extracción, recopila la cantidad requerida de aprobaciones de sus colegas, corrige lo que se necesita y luego inicia las compilaciones. Si tienen éxito, la solicitud de extracción se fusiona en desarrollo. En este caso, no hay reinicios adicionales, pero la respuesta se ralentiza considerablemente. Como desarrollador, cuando abro una solicitud de extracción, inmediatamente quiero ver si va a funcionar. Por ejemplo, si una prueba falla, es necesario solucionarla rápidamente. En el caso de una compilación retrasada, la retroalimentación se ralentiza y, por tanto, todo el desarrollo. Esto tampoco nos convenía.

Como resultado, solo quedó la tercera opción: bicicleta. Todo nuestro código, todas nuestras fuentes se almacenan en un repositorio en el servidor de Bitbucket. En consecuencia, tuvimos que desarrollar un complemento para Bitbucket.

Evolución de la CI en el equipo de desarrollo móvil

Este complemento anula el mecanismo de fusión de solicitudes de extracción. El comienzo es estándar: se abre el PR, se inician todos los ensamblajes y se completa la revisión del código. Pero una vez que se completa la revisión del código y el desarrollador decide hacer clic en "fusionar", el complemento verifica en qué estado de desarrollo se ejecutaron las comprobaciones. Si el desarrollo se actualizó después de las compilaciones, el complemento no permitirá que dicha solicitud de extracción se fusione en la rama principal. Simplemente reiniciará las compilaciones de un desarrollo relativamente reciente.

Evolución de la CI en el equipo de desarrollo móvil

En nuestro ejemplo con cambios conflictivos, dichas compilaciones fallarán debido a un error de compilación. En consecuencia, el desarrollador de la característica B tendrá que corregir el código, reiniciar las comprobaciones y luego el complemento aplicará automáticamente la solicitud de extracción.

Antes de implementar este complemento, teníamos un promedio de 2,7 ejecuciones de revisión por solicitud de extracción. Con el complemento hubo 3,6 lanzamientos. Esto nos vino bien.

Vale la pena señalar que este complemento tiene un inconveniente: solo reinicia la compilación una vez. Es decir, todavía hay una pequeña ventana a través de la cual pueden desarrollarse cambios contradictorios. Pero la probabilidad de que esto ocurra es baja, e hicimos este equilibrio entre el número de inicios y la probabilidad de fracaso. En dos años sólo disparó una vez, por lo que probablemente no fue en vano.

Nos tomó dos semanas escribir la primera versión del complemento Bitbucket.

Nuevos cheques

Mientras tanto, nuestro equipo siguió creciendo. Se han agregado nuevos controles.

Pensamos: ¿por qué cometer errores si se pueden prevenir? Y es por eso que implementaron análisis de código estático. Comenzamos con lint, que está incluido en el SDK de Android. Pero en ese momento no sabía nada de trabajar con código Kotlin, y ya teníamos el 75% de la aplicación escrita en Kotlin. Por lo tanto, se agregaron los integrados a la pelusa. Comprobaciones de Android Studio.

Para hacer esto, tuvimos que pervertir mucho: tomar Android Studio, empaquetarlo en Docker y ejecutarlo en CI con un monitor virtual, para que piense que se está ejecutando en una computadora portátil real. Pero funcionó.

También fue durante este tiempo que empezamos a escribir mucho. pruebas de instrumentación e implementado prueba de captura de pantalla. Aquí es cuando se genera una captura de pantalla de referencia para una vista pequeña separada, y la prueba consiste en tomar una captura de pantalla de la vista y compararla con la estándar directamente píxel por píxel. Si hay una discrepancia, significa que el diseño salió mal en alguna parte o que algo anda mal en los estilos.

Pero las pruebas de instrumentación y las pruebas de captura de pantalla deben ejecutarse en dispositivos: en emuladores o en dispositivos reales. Teniendo en cuenta que hay muchas pruebas y se realizan con frecuencia, se necesita una granja completa. Iniciar su propia granja requiere demasiada mano de obra, por lo que encontramos una opción ya preparada: Firebase Test Lab.

Laboratorio de pruebas de Firebase

Se eligió porque Firebase es un producto de Google, lo que significa que debería ser confiable y es poco probable que muera alguna vez. Los precios son razonables: 5 dólares por hora de funcionamiento de un dispositivo real, 1 dólar por hora de funcionamiento de un emulador.

Se necesitaron aproximadamente tres semanas para implementar Firebase Test Lab en nuestro CI.

Pero el equipo siguió creciendo y, lamentablemente, Firebase empezó a decepcionarnos. En ese momento no tenía ningún SLA. A veces, Firebase nos hacía esperar hasta que la cantidad requerida de dispositivos estuviera libre para las pruebas y no comenzaba a ejecutarlas de inmediato, como queríamos. La cola de espera duró hasta media hora, lo cual es mucho tiempo. Se realizaron pruebas de instrumentación en cada RP, los retrasos realmente ralentizaron el desarrollo y luego la factura mensual llegó con una suma redonda. En general, se decidió abandonar Firebase y trabajar internamente, ya que el equipo había crecido bastante.

Docker + Python + bash

Tomamos Docker, le incorporamos emuladores, escribimos un programa simple en Python, que en el momento adecuado inicia la cantidad requerida de emuladores en la versión correcta y los detiene cuando es necesario. Y, por supuesto, un par de scripts bash: ¿dónde estaríamos sin ellos?

Nos llevó cinco semanas crear nuestro propio entorno de prueba.

Como resultado, para cada solicitud de extracción había una extensa lista de comprobaciones de bloqueo de fusión:

  • montaje ARCA;
  • Pruebas conjuntas;
  • Hilas;
  • Comprobaciones de Android Studio;
  • Pruebas de instrumentación;
  • Pruebas de captura de pantalla.

Esto evitó muchas posibles averías. Técnicamente todo funcionó, pero los desarrolladores se quejaron de que la espera por los resultados era demasiado larga.

¿Cuánto tiempo es demasiado? Cargamos datos de Bitbucket y TeamCity en el sistema de análisis y nos dimos cuenta de que tiempo medio de espera 45 minutos. Es decir, un desarrollador, al abrir una solicitud de extracción, espera en promedio 45 minutos para obtener los resultados de la compilación. En mi opinión, esto es mucho y no se puede trabajar así.

Por supuesto, decidimos acelerar todas nuestras compilaciones.

aceleremos

Dado que las compilaciones suelen estar en cola, lo primero que hacemos es compré más hardware — el desarrollo extensivo es el más simple. Las compilaciones dejaron de hacer cola, pero el tiempo de espera disminuyó solo ligeramente, porque algunas comprobaciones tardaron mucho tiempo.

Eliminar cheques que tardan demasiado

Nuestra Integración Continua podría detectar este tipo de errores y problemas.

  • No va a. CI puede detectar un error de compilación cuando algo no se compila debido a cambios contradictorios. Como ya dije, entonces nadie puede ensamblar nada, el desarrollo se detiene y todos se ponen nerviosos.
  • Error en el comportamiento. Por ejemplo, cuando la aplicación está creada, pero falla cuando presiona un botón, o el botón no se presiona en absoluto. Esto es malo porque un error de este tipo puede afectar al usuario.
  • Error en el diseño. Por ejemplo, se hace clic en un botón, pero se ha movido 10 píxeles hacia la izquierda.
  • Aumento de la deuda técnica.

Después de mirar esta lista, nos dimos cuenta de que sólo los dos primeros puntos son críticos. Primero queremos detectar estos problemas. Los errores en el diseño se descubren en la etapa de revisión del diseño y luego se pueden corregir fácilmente. Lidiar con la deuda técnica requiere un proceso y una planificación separados, por lo que decidimos no probarlo en una solicitud de extracción.

Basándonos en esta clasificación, modificamos toda la lista de controles. Pelusa tachada y pospuso su lanzamiento de la noche a la mañana: sólo para producir un informe sobre cuántos problemas había en el proyecto. Acordamos trabajar por separado con la deuda técnica, y Las comprobaciones de Android Studio fueron abandonadas por completo. Android Studio en Docker para ejecutar inspecciones suena interesante, pero causa muchos problemas de soporte. Cualquier actualización a las versiones de Android Studio significa una lucha con errores incomprensibles. También era difícil soportar pruebas de captura de pantalla, porque la biblioteca no era muy estable y había falsos positivos. Las pruebas de captura de pantalla se han eliminado de la lista de verificación..

Como resultado, nos quedamos con:

  • montaje ARCA;
  • Pruebas conjuntas;
  • Pruebas de instrumentación.

Caché remoto de Gradle

Sin controles estrictos, todo mejoró. ¡Pero no hay límite para la perfección!

Nuestra aplicación ya estaba dividida en unos 150 módulos Gradle. El caché remoto de Gradle generalmente funciona bien en este caso, así que decidimos probarlo.

La caché remota de Gradle es un servicio que puede almacenar en caché artefactos de compilación para tareas individuales en módulos individuales. Gradle, en lugar de compilar el código, usa HTTP para acceder al caché remoto y preguntar si alguien ya realizó esta tarea. En caso afirmativo, simplemente descarga el resultado.

Ejecutar la caché remota de Gradle es fácil porque Gradle proporciona una imagen de Docker. Logramos hacer esto en tres horas.

Todo lo que tenías que hacer era iniciar Docker y escribir una línea en el proyecto. Pero aunque se puede iniciar rápidamente, hará falta bastante tiempo para que todo funcione bien.

A continuación se muestra el gráfico de errores de caché.

Evolución de la CI en el equipo de desarrollo móvil

Al principio, el porcentaje de errores de caché era de aproximadamente 65. Después de tres semanas, logramos aumentar este valor al 20%. Resultó que las tareas que recopila la aplicación de Android tienen dependencias transitivas extrañas, por lo que Gradle perdió el caché.

Al conectar el caché, aceleramos enormemente la compilación. Pero además del montaje, también hay pruebas de instrumentación, y llevan mucho tiempo. Quizás no sea necesario ejecutar todas las pruebas para cada solicitud de extracción. Para averiguarlo, utilizamos el análisis de impacto.

Análisis de impacto

En una solicitud de extracción, recopilamos git diff y buscamos los módulos Gradle modificados.

Evolución de la CI en el equipo de desarrollo móvil

Tiene sentido ejecutar únicamente pruebas de instrumentación que verifiquen los módulos modificados y todos los módulos que dependen de ellos. No tiene sentido ejecutar pruebas para módulos vecinos: el código no ha cambiado y nada puede romperse.

Las pruebas de instrumentación no son tan simples porque deben ubicarse en el módulo de Aplicación de nivel superior. Usamos heurística con análisis de código de bytes para comprender a qué módulo pertenece cada prueba.

Actualizar el funcionamiento de las pruebas de instrumentación para que solo prueben los módulos involucrados tomó aproximadamente ocho semanas.

Las medidas para acelerar las inspecciones han funcionado con éxito. De 45 minutos pasamos a unos 15. Ya es normal esperar un cuarto de hora para una construcción.

Pero ahora los desarrolladores han comenzado a quejarse de que no entienden qué compilaciones se están lanzando, dónde ver el registro, por qué la compilación está en rojo, qué prueba falló, etc.

Evolución de la CI en el equipo de desarrollo móvil

Los problemas con la retroalimentación ralentizan el desarrollo, por lo que intentamos proporcionar la información más clara y detallada sobre cada RP y compilación. Comenzamos con comentarios en Bitbucket al PR, indicando qué compilación había fallado y por qué, y escribimos mensajes específicos en Slack. Al final, creamos un panel de relaciones públicas para la página con una lista de todas las compilaciones que se están ejecutando actualmente y su estado: en cola, en ejecución, fallada o completada. Puede hacer clic en la compilación y acceder a su registro.

Evolución de la CI en el equipo de desarrollo móvil

Se dedicaron seis semanas a recibir comentarios detallados.

Planes

Pasemos a la historia reciente. Una vez resuelto el problema de la retroalimentación, alcanzamos un nuevo nivel: decidimos construir nuestra propia granja de emuladores. Cuando hay muchas pruebas y emuladores, son difíciles de gestionar. Como resultado, todos nuestros emuladores se trasladaron al clúster k8s con gestión de recursos flexible.

Además, hay otros planes.

  • Devolver pelusa (y otros análisis estáticos). Ya estamos trabajando en esta dirección.
  • Ejecute todo en un bloqueador de relaciones públicas pruebas de extremo a extremo en todas las versiones del SDK.

Entonces, hemos rastreado la historia del desarrollo de la Integración Continua en Avito. Ahora quiero dar algunos consejos desde un punto de vista experimentado.

Tips

Si pudiera dar un solo consejo sería este:

¡Tenga cuidado con los scripts de shell!

Bash es una herramienta muy flexible y poderosa, es muy conveniente y rápido para escribir scripts. Pero con ello se puede caer en una trampa y, lamentablemente, nosotros caímos en ella.

Todo comenzó con scripts simples que se ejecutaron en nuestras máquinas de compilación:

#!/usr/bin/env bash
./gradlew assembleDebug

Pero, como saben, todo se desarrolla y se vuelve más complicado con el tiempo: ejecutemos un script desde otro, pasemos algunos parámetros allí; al final tuvimos que escribir una función que determine en qué nivel de anidamiento de bash nos encontramos ahora. para insertar las comillas necesarias, para que todo comience.

Evolución de la CI en el equipo de desarrollo móvil

Puede imaginarse los costos laborales para el desarrollo de dichos guiones. Te aconsejo que no caigas en esta trampa.

¿Qué se puede reemplazar?

  • Cualquier lenguaje de scripting. Escribir a Secuencia de comandos Python o Kotlin más conveniente porque se trata de programación, no de scripts.
  • O describir toda la lógica de construcción en el formulario Tareas personalizadas de gradle para tu proyecto.

Decidimos elegir la segunda opción y ahora estamos eliminando sistemáticamente todos los scripts de bash y escribiendo muchas tareas personalizadas de Gradle.

Consejo nº 2: almacene la infraestructura en código.

Es conveniente cuando la configuración de Integración continua no se almacena en la interfaz de usuario de Jenkins o TeamCity, etc., sino en forma de archivos de texto directamente en el repositorio del proyecto. Esto da versionabilidad. No será difícil revertir o compilar el código en otra rama.

Los scripts se pueden almacenar en un proyecto. ¿Qué hacer con el medio ambiente?

Consejo nº 3: Docker puede ayudar con el medio ambiente.

Definitivamente ayudará a los desarrolladores de Android; desafortunadamente, iOS aún no tiene uno.

Este es un ejemplo de un archivo acoplable simple que contiene jdk y android-sdk:

FROM openjdk:8

ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip" 
    ANDROID_HOME="/usr/local/android-sdk" 
    ANDROID_VERSION=26 
    ANDROID_BUILD_TOOLS_VERSION=26.0.2

# Download Android SDK
RUN mkdir "$ANDROID_HOME" .android 
    && cd "$ANDROID_HOME" 
    && curl -o sdk.zip $SDK_URL 
    && unzip sdk.zip 
    && rm sdk.zip 
    && yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses

# Install Android Build Tool and Libraries
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" 
    "platforms;android-${ANDROID_VERSION}" 
    "platform-tools"

RUN mkdir /application
WORKDIR /application

Después de escribir este archivo Docker (te contaré un secreto, no es necesario que lo escribas, simplemente lo extraes de GitHub) y ensamblas la imagen, obtienes una máquina virtual en la que puedes construir la aplicación. y ejecutar pruebas Junit.

Las dos razones principales por las que esto tiene sentido son la escalabilidad y la repetibilidad. Con Docker, puede generar rápidamente una docena de agentes de compilación que tendrán exactamente el mismo entorno que el anterior. Esto facilita mucho la vida de los ingenieros de CI. Es bastante fácil insertar el SDK de Android en la ventana acoplable, pero con los emuladores es un poco más difícil: tendrás que trabajar un poco más (o descargar el terminado desde GitHub nuevamente).

Consejo nº 4: no olvide que las inspecciones no se hacen por el bien de las inspecciones, sino por las personas.

Los comentarios rápidos y, lo más importante, claros son muy importantes para los desarrolladores: qué falló, qué prueba falló, dónde puedo ver el registro de compilación.

Consejo #5: Sea pragmático al desarrollar la Integración Continua.

Comprenda claramente qué tipos de errores desea evitar, cuántos recursos, tiempo y tiempo de computadora está dispuesto a gastar. Los controles que tardan demasiado pueden, por ejemplo, posponerse de la noche a la mañana. Y aquellos que detectan errores no muy importantes deberían abandonarse por completo.

Consejo nº 6: utilice herramientas ya preparadas.

Actualmente hay muchas empresas que ofrecen CI en la nube.

Evolución de la CI en el equipo de desarrollo móvil

Esta es una buena solución para equipos pequeños. No necesita soporte para nada, solo pague un poco de dinero, cree su aplicación e incluso ejecute pruebas de instrumentación.

Consejo nº 7: en un equipo grande, las soluciones internas son más rentables.

Pero tarde o temprano, a medida que el equipo crezca, las soluciones internas serán más rentables. Hay un problema con estas decisiones. Existe una ley de rendimientos decrecientes en economía: en cualquier proyecto, cada mejora posterior es cada vez más difícil y requiere cada vez más inversión.

La economía describe toda nuestra vida, incluida la Integración Continua. Construí un cronograma de costos laborales para cada etapa de desarrollo de nuestra Integración Continua.

Evolución de la CI en el equipo de desarrollo móvil

Está claro que cualquier mejora es cada vez más difícil. Al observar este gráfico, se puede comprender que la integración continua debe desarrollarse de acuerdo con el crecimiento del tamaño del equipo. Para un equipo de dos personas, dedicar 50 días a desarrollar una granja de emuladores interna es una idea mediocre. Pero al mismo tiempo, para un equipo grande, no hacer ninguna Integración Continua también es una mala idea, porque hay problemas de integración, reparación de comunicación, etc. tomará aún más tiempo.

Empezamos con la idea de que la automatización es necesaria porque las personas son caras, cometen errores y son vagas. Pero la gente también automatiza. Por tanto, los mismos problemas se aplican a la automatización.

  • La automatización es cara. Recuerda el horario laboral.
  • Cuando se trata de automatización, la gente comete errores.
  • A veces da mucha pereza automatizar, porque todo funciona así. ¿Por qué mejorar algo más, por qué toda esta Integración Continua?

Pero tengo estadísticas: se detectan errores en el 20% de los ensamblajes. Y esto no se debe a que nuestros desarrolladores escriban mal el código. Esto se debe a que los desarrolladores confían en que si cometen algún error, no terminará en desarrollo, sino que será detectado por comprobaciones automáticas. En consecuencia, los desarrolladores pueden dedicar más tiempo a escribir código y cosas interesantes, en lugar de ejecutar y probar algo localmente.

Practica la integración continua. Pero con moderación.

Por cierto, Nikolai Nesterov no sólo hace excelentes informes, sino que también es miembro del comité del programa. AplicacionesConf y ayuda a otros a preparar discursos significativos para usted. La integridad y utilidad del programa de la próxima conferencia se puede evaluar por temas en calendario. Y para más detalles, visite Infospace del 22 al 23 de abril.

Fuente: habr.com

Añadir un comentario