Experiencia CICD móvil: un estándar rápido para muchas aplicaciones móviles

Experiencia CICD móvil: un estándar rápido para muchas aplicaciones móviles
Me gustaría hablar sobre la integración y entrega continua de aplicaciones móviles mediante fastlane. Cómo implementamos CI/CD en todas las aplicaciones móviles, cómo llegamos allí y qué sucedió al final.

Ya hay suficiente material en la red sobre la herramienta que tanto nos faltaba al principio, por lo que deliberadamente no describiré la herramienta en detalle, sino que solo me referiré a lo que teníamos entonces:

El artículo consta de dos partes:

  • Antecedentes de la aparición del CI/CD móvil en la empresa
  • Solución técnica para implementar CI/CD para aplicaciones N

La primera parte es más nostalgia por los viejos tiempos y la segunda es una experiencia que puedes aplicar a ti mismo.

Así sucedió históricamente

Año 2015

Recién empezamos a desarrollar aplicaciones móviles, luego no sabíamos nada sobre integración continua, sobre DevOps y otras cosas de moda. Cada actualización de la aplicación fue implementada por el propio desarrollador desde su máquina. Y si para Android es bastante sencillo: ensamblado, firmado .apk y lo subimos a Google Developer Console, luego, para iOS, la herramienta de distribución a través de Xcode nos dejó noches maravillosas: los intentos de descargar el archivo a menudo terminaban en errores y teníamos que volver a intentarlo. Resultó que el desarrollador más avanzado no escribe código varias veces al mes, sino que publica la aplicación.

Año 2016

Crecimos, ya teníamos pensamientos sobre cómo liberar a los desarrolladores de un día entero para un lanzamiento, y también apareció una segunda aplicación, que solo nos empujó más hacia la automatización. Ese mismo año instalamos Jenkins por primera vez y escribimos un montón de scripts aterradores, muy similares a los que muestra fastlane en su documentación.

$ xcodebuild clean archive -archivePath build/MyApp 
    -scheme MyApp

$ xcodebuild -exportArchive 
                        -exportFormat ipa 
                        -archivePath "build/MyApp.xcarchive" 
                        -exportPath "build/MyApp.ipa" 
                        -exportProvisioningProfile "ProvisioningProfileName"

$ cd /Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/

$ ./altool —upload-app 
-f {abs path to your project}/build/{release scheme}.ipa  
-u "[email protected]" 
-p "PASS_APPLE_ID"

Desafortunadamente, hasta ahora sólo nuestros desarrolladores sabían cómo funcionan estos scripts y por qué se necesita esta pila interminable de claves, y cuando algo volvió a fallar, tuvieron "tardes maravillosas" para analizar los registros.

Año 2017

Este año aprendimos que existe el fastlane. No había tanta información como hay ahora: cómo iniciar uno, cómo utilizarlo. Y la herramienta en sí todavía era tosca en ese momento: los errores constantes solo nos decepcionaban y era difícil creer en la automatización mágica que prometían.

Sin embargo, las principales utilidades incluidas en el núcleo fastlane son gym и pilot, logramos iniciarlo.

Nuestros guiones se han mejorado un poco.

$ fastlane gym  —-workspace "Example.xcworkspace" 
                --scheme "AppName" 
                —-buildlog_path "/tmp" 
                -—clean

Se han mejorado, aunque sólo sea porque no todos los parámetros necesarios para xcodebuild, debe indicar - gym comprenderá de forma independiente dónde y qué se encuentra. Y para un ajuste más preciso, puede especificar las mismas claves que en xcodebuild, sólo la denominación de las claves es más clara.

Esta vez, gracias al gimnasio y al formateador xcpretty incorporado, los registros de compilación se han vuelto mucho más legibles. Esto comenzó a ahorrar tiempo en la reparación de ensamblajes rotos y, a veces, el equipo de lanzamiento podía resolverlo por sí solo.

Lamentablemente, las mediciones de velocidad de montaje xcodebuild и gym No lo hicimos, pero confiaremos en la documentación: hasta un 30% de aceleración.

Proceso único para todas las aplicaciones.

Año 2018 y presente

En 2018, el proceso de creación e implementación de aplicaciones se trasladó completamente a Jenkins, los desarrolladores dejaron de publicar desde sus máquinas y solo el equipo de lanzamiento tenía derecho a realizar el lanzamiento.

Ya queríamos mejorar el lanzamiento de pruebas y análisis estáticos, y nuestros scripts crecieron y crecieron. Creció y cambió junto con nuestras aplicaciones. En ese momento había alrededor de 10 aplicaciones, considerando que tenemos dos plataformas, son alrededor de 20 scripts “vivos”.

Cada vez que queríamos agregar un nuevo paso al script, teníamos que copiar y pegar las piezas en todos los scripts de shell. Quizás podríamos haber trabajado con más cuidado, pero a menudo estos cambios terminaron en errores tipográficos, lo que se convirtió en tardes para que el equipo de lanzamiento corrigiera los scripts y descubriera qué tipo inteligente agregó este comando y qué hace realmente. En general, no se puede decir que los guiones de montaje de una plataforma fueran al menos algo similares. Aunque ciertamente hicieron lo mismo.

Para iniciar un proceso para una nueva aplicación, era necesario dedicar un día a seleccionar una versión "nueva" de estos scripts, depurarla y decir "sí, funciona".

En el verano de 2018 volvimos a mirar hacia la vía rápida que aún está en desarrollo.

Tarea n.º 1: resumir todos los pasos del guión y reescribirlos en Fastfile

Cuando empezamos, nuestros guiones parecían una calza que constaba de todos los escalones y muletas en un guión shell en Jenkins. Todavía no hemos cambiado a canalización y división por etapas.

Analizamos lo que tenemos e identificamos 4 pasos que se ajustan a la descripción de nuestro CI/CD:

  • construir: instalar dependencias, ensamblar el archivo,
  • prueba: ejecutar pruebas unitarias de desarrollador, calcular la cobertura,
  • sonar: lanza todos los linters y envía informes a SonarQube,
  • implementar: enviar un artefacto a alfa (TestFlight).

Y si no entras en detalles, omitiendo las claves utilizadas en las acciones, obtendrás este Fastfile:

default_platform(:ios)

platform :ios do
  before_all do
    unlock
  end

  desc "Build stage"
  lane :build do
    match
    prepare_build
    gym
  end

  desc "Prepare build stage: carthage and cocoapods"
  lane :prepare_build do
    pathCartfile = ""
    Dir.chdir("..") do
      pathCartfile = File.join(Dir.pwd, "/Cartfile")
    end
    if File.exist?(pathCartfile)
      carthage
    end
    pathPodfile = ""
    Dir.chdir("..") do
      pathPodfile = File.join(Dir.pwd, "/Podfile")
    end
    if File.exist?(pathPodfile)
      cocoapods
    end
  end

  desc "Test stage"
  lane :test do
    scan
    xcov
  end

  desc "Sonar stage (after run test!)"
  lane :run_sonar do
    slather
    lizard
    swiftlint
    sonar
  end

  desc "Deploy to testflight stage"
  lane :deploy do
    pilot
  end

  desc "Unlock keychain"
  private_lane :unlock do
    pass = ENV['KEYCHAIN_PASSWORD']
    unlock_keychain(
      password: pass
    )
  end
end

De hecho, nuestro primer Fastfile resultó ser monstruoso, considerando algunas de las muletas que aún necesitábamos y la cantidad de parámetros que sustituimos:

lane :build do
carthage(
  command: "update",
  use_binaries: false,
  platform: "ios",
  cache_builds: true)
cocoapods(
  clean: true,
    podfile: "./Podfile",
    use_bundle_exec: false)

gym(
  workspace: "MyApp.xcworkspace",
  configuration: "Release",
  scheme: "MyApp",
  clean: true,
  output_directory: "/build",
  output_name: "my-app.ipa")
end 

lane :deploy do
 pilot(
  username: "[email protected]",
  app_identifier: "com.example.app",
  dev_portal_team_id: "TEAM_ID_NUMBER_DEV",
  team_id: "ITS_TEAM_ID")
end

En el ejemplo anterior, solo necesitamos especificar algunos de los parámetros: estos son los parámetros de compilación (esquema, configuración, nombres de perfiles de aprovisionamiento, así como parámetros de distribución): ID de Apple de la cuenta de desarrollador, contraseña, ID de la aplicación, etc. en. Como primera aproximación, colocamos todas estas claves en archivos especiales: Gymfile, Matchfile и Appfile.

Ahora en Jenkins puedes llamar comandos cortos que no nublan la vista y son fácilmente legibles a simple vista:

# fastlane ios <lane_name>

$ fastlane ios build
$ fastlane ios test
$ fastlane ios run_sonar
$ fastlane ios deploy

Hurra, somos geniales

¿Qué obtuviste? Comandos claros para cada paso. Scripts limpios, ordenados cuidadosamente en archivos fastlane. Regocijados, corrimos hacia los desarrolladores para pedirles que agregaran todo lo que necesitaban a sus repositorios.

Pero con el tiempo nos dimos cuenta de que encontraríamos las mismas dificultades: todavía tendríamos 20 scripts ensambladores que de una forma u otra comenzarían a vivir sus propias vidas, sería más difícil editarlos, ya que los scripts se trasladarían a los repositorios. y no teníamos acceso allí. Y, en general, así no será posible solucionar nuestro dolor.

Experiencia CICD móvil: un estándar rápido para muchas aplicaciones móviles

Tarea n.º 2: obtener un único Fastfile para N aplicaciones

Ahora parece que resolver el problema no es tan difícil: configura las variables y vámonos. Sí, de hecho, así se solucionó el problema. Pero en el momento en que lo arruinamos, no teníamos experiencia en fastlane en sí, ni en Ruby, en el que está escrito fastlane, ni ejemplos útiles en la red; todos los que escribieron sobre fastlane en ese momento se limitaron a un ejemplo para una aplicación para un desarrollador.

Fastlane puede manejar variables de entorno y ya lo hemos probado configurando la contraseña del llavero:

ENV['KEYCHAIN_PASSWORD']

Después de mirar nuestros scripts, identificamos las partes comunes:

#for build, test and deploy
APPLICATION_SCHEME_NAME=appScheme
APPLICATION_PROJECT_NAME=app.xcodeproj
APPLICATION_WORKSPACE_NAME=app.xcworkspace
APPLICATION_NAME=appName

OUTPUT_IPA_NAME=appName.ipa

#app info
APP_BUNDLE_IDENTIFIER=com.example.appName
[email protected]
TEAM_ID=ABCD1234
FASTLANE_ITC_TEAM_ID=123456789

Ahora, para comenzar a usar estas claves en archivos fastlane, teníamos que descubrir cómo entregarlas allí. Fastlane tiene una solución para esto: cargando variables a través de dotenv. La documentación dice que si es importante para usted cargar claves para diferentes propósitos, cree varios archivos de configuración en el directorio fastlane .env, .env.default, .env.development.

Y luego decidimos usar esta biblioteca de manera un poco diferente. Coloquemos en el repositorio de desarrolladores no los scripts fastlane y su metainformación, sino las claves únicas de esta aplicación en el archivo. .env.appName.

Ellos mismos Fastfile, Appfile, Matchfile и Gymfile, lo ocultamos en un repositorio separado. Allí se ocultó un archivo adicional con claves de contraseña de otros servicios: .env.
Puedes ver un ejemplo. aquí.

Experiencia CICD móvil: un estándar rápido para muchas aplicaciones móviles

En CI, la llamada no ha cambiado mucho; se ha agregado una clave de configuración para una aplicación específica:

# fastlane ios <lane_name> --env appName

$ fastlane ios build --env appName
$ fastlane ios test --env appName
$ fastlane ios run_sonar --env appName
$ fastlane ios deploy --env appName

Antes de ejecutar los comandos, cargamos nuestro repositorio con scripts. No se ve tan bien:

git clone [email protected]/FastlaneCICD.git fastlane_temp

cp ./fastlane_temp/fastlane/* ./fastlane/
cp ./fastlane_temp/fastlane/.env fastlane/.env

Dejé esta solución por ahora, aunque Fastlane tiene una solución para descargar Fastfile a través de DE ACTUAR! import_from_git, pero solo funciona para Fastfile, pero no para otros archivos. Si quieres "realmente hermoso", puedes escribir el tuyo propio. action.

Se hizo un conjunto similar para aplicaciones de Android y ReactNative, los archivos están en el mismo repositorio, pero en diferentes ramas. iOS, android и react_native.

Cuando el equipo de lanzamiento quiere agregar algún paso nuevo, los cambios en el script se registran a través de MR en git, ya no hay necesidad de buscar a los culpables de los scripts rotos y, en general, ahora hay que intentar romperlos.

Ahora eso es todo seguro.

Anteriormente, dedicábamos tiempo a mantener todos los scripts, actualizarlos y corregir todas las consecuencias de las actualizaciones. Fue muy decepcionante cuando las razones de los errores y el tiempo de inactividad en las versiones eran simples errores tipográficos que eran tan difíciles de seguir en la confusión de scripts de shell. Ahora esos errores se reducen al mínimo. Los cambios se implementan en todas las aplicaciones a la vez. Y se necesitan 15 minutos para incluir una nueva aplicación en el proceso: configurar una canalización de plantilla en CI y agregar las claves al repositorio del desarrollador.

Parece que el punto con Fastfile para Android y la firma de la aplicación sigue sin explicarse; si el artículo es interesante, escribiré una continuación. Estaré encantado de ver tus preguntas o sugerencias "¿cómo solucionarías este problema?" en los comentarios o en Telegram. bashkirova.

Fuente: habr.com

Añadir un comentario