Loading Likes...

Gracias a los cambios que nos ha traído el implementar una nueva arquitectura en los proyectos de iOS, nuestras app son más fáciles de testear.

Una buena batería de tests es algo que todo proyecto ágil debería tener, pero no se suele cumplir por diferentes motivos: bases de código difíciles de testear, deuda técnica, etc.

Dos maneras de poner solución a un proyecto sin tests son:

  • Al desarrollar una nueva funcionalidad hacerlo usando TDD, aunque si no te gusta esta técnica siempre puedes desarrollar pensando en que sea lo más desacoplado posible para que hacer tests no suponga un suplicio. En cualquier caso siempre es recomendado crear el test antes de la funcionalidad.
  • Si tocas base de código antiguo sin testear aprovecha la oportunidad para refactorizarlo y propocionar una batería de tests.

En el caso de la app de habitissimo tenemos los dos casos. Aprovechando que empezamos el desarrollo de una arquitectura de cero para la app de particulares, decidimos dar el paso natural incluyendo integración continua (CI). En nuestro caso usamos Feature branching, lo cual implica que debemos garantizar que la rama master y todos los pull request pasen los tests.

Al tratar de hacer CI con el sistema cerrado de Apple nos encontramos con que sólo se pueden lanzar los tests con un ordenador con macOS instalado. Pero este es el único impedimento que hay.

Instalación y configuración de las herramientas

Runners de GitLab

En habitissimo usamos GitLab self-hosted para mantener nuestros repositorios centralizados y es el encargado de ejecutar los runners. Estos runners los tenemos situados en las máquinas de los desarrolladores del equipo técnico. Los runners permiten a GitLab ejecutar los tests o scripts definidos en el fichero .gitlab-ci.yml.

El primer paso para montar el sistema de integración continua es instalar gitlab-runner en la máquina que quieres usar para ejecutar lo que le mande GitLab. Después hay que registrarlo usando sudo gitlab-runner register que no es más que seguir un par de pasos. Ojo: cuando te pida el runner-executor hay que poner shell ya que docker todavía no va muy fino sobre macOS.
Este proceso es bastante sencillo y lo puedes encontrar en la documentación de GitLab.

Si usáis runners compartidos hay que asegurarse de que el proyecto de iOS se ejecute sólo en aquellos que tengan como sistema operativo macOS. Para ello podéis configurar el proyecto de GitLab para que se ejecute sólo en runners específicos o registrar los runners con una etiqueta que identifique que pueden compilar iOS.

El runner puede ser el ordenador con el que desarrollas pero no lo aconsejamos porque te quitará potencia.

Fastlane

Fastlane como se define en Github es The easiest way to automate building and releasing your iOS and Android apps. Facilita mucho el proceso de generar una build, pasar los tests y automatizar los certificados necesarios para poder compilar entre muchas más funcionalidades.

Para instalar Fastlane podéis encontrar la documentación oficial en Github que lo explica bastante bien.

Una vez instalado tenemos que configurarlo para nuestro proyecto en concreto. En este caso vamos al directorio raíz, donde podemos encontrar el .xcodeproj o el .xcworkspace. Una vez en la ráiz, debemos ejecutar el comando $ fastlane init y seguir las instrucciones.

Al final veremos que nos ha creado un fichero bajo esta ruta /fastlane/Fastfile que tiene todas las lanes. Estas lanes no son más que instrucciones secuenciales en las que podemos poner comandos sh, plugins de terceros y herramientas que vienen en el propio fastlane.

En la documentación de fastlane podéis ver todas las virguerías que puedes hacer con está herramienta.

Fallos que nos encontramos

Una vez tenemos configurados GitLabFastlane, es hora de hacer un push con nuevos commits. Al hacer esto veremos como en GitLab hay un pipeline activo.

En nuestro caso enseguida falló 😅:

Running with gitlab-ci-multi-runner 9.4.2 (6d06f2e)
  on iOS Runner (XXXXXXXX)
Using Shell executor...
Running on neuromancer...
xcrun: error: invalid active developer path (/Library/Developer/
      CommandLineTools), missing xcrun 
      at: /Library/Developer/CommandLineTools/usr/bin/xcrun
ERROR: Job failed: exit status 1

Oh shit, no hemos instalado las herramientas de desarrollo de XCode. Solución: xcode-select --install. Volvemos a ejecutar el pipeline desde GitLab.

Pero volvió a fallar 😔:

Running with gitlab-ci-multi-runner 9.4.2 (6d06f2e)
  on iOS Runner (XXXXXXXX)
Using Shell executor...
Running on neuromancer...


Agreeing to the Xcode/iOS license requires admin privileges, 
  please run “sudo xcodebuild -license” and then retry this command.


ERROR: Job failed: exit status 1

Bueno no es tan grave, solo hay que aceptar los términos y condiciones de Apple. Y volvemos a ejecutar.

Y ocurrió de nuevo 😭:

Running with gitlab-ci-multi-runner 9.4.2 (6d06f2e)
  on iOS Runner (XXXXXXXX)
Using Shell executor...
Running on neuromancer...
Cloning repository...
Cloning into '/Users/Habitissimo/builds/XXXXXXXX/
    0/ios/habitissimo-customers-swift'...
Checking out 6306d98d as master...
Skipping Git submodules setup
ERROR: Job failed: exit status 1

Esta vez vete a saber tu por qué… Pero gracias a Googlearlo encontramos la solución que simplemente es añadir unset cd en el fichero .bash_profile

Volvemos a ejecutar.

Y sorpresa, falla de nuevo 😱:

Exit status of command 'bundle exec pod install' was 1 instead of 0.
/System/Library/Frameworks/Ruby.framework/Versions
     2.0/usr/lib/ruby/2.0.0/universal-darwin16/rbconfig.rb:213: 
bundler: failed to load command: pod (/usr/local/bin/pod)
Gem::LoadError: cocoapods is not part of the bundle. 
  Add it to your Gemfile.
  /Library/Ruby/Gems/2.0.0/gems/bundler-1.15.3/lib/
       bundler/rubygems_integration.rb:377:in 
         `block (2 levels) in replace_gem'
  /usr/local/bin/pod:22:in `'

Bueno parece que tenemos que crear un fichero en el proyecto Gemfile con lo siguiente:

source "https://rubygems.org"
 
gem "fastlane"
gem "cocoapods"

Y a ver si ahora es la buena… pues va a ser que no. Ahora ya teníamos solucionados los problemas de configuración del runner, así que nos faltaba corregir los problemas de configuración del Fastfile (que fueron rápidos de solucionar). La solución a los problemas que nos surgieron las pudimos encontrar fácilmente en la documentación de Fastlane y en los propios mensajes de error que salen por consola.

Nuestra configuración

En nuestro caso de momento es muy sencillo:

stages:
  - test

test:
  stage: test
  script:
    - bundle exec fastlane test
  tags:
    - ios
  only:
    - triggers
    - master

trigger_test:
  script:
    - sh ci/trigger-mr.sh
  tags:
    - ios
  except:
    - triggers

No tiene mucho misterio. Como GitLab por defecto realizaría los tests en cada push lo limitamos a que lo haga cada vez que se cambie algo en la rama master o cuando un trigger sea llamado. El trigger_test lanza un script para mirar si hay algún pull request activo de la rama que se ha modificado. Si la hay, este script activa el trigger para pasar los tests.

El contenido del Fastfile es más extenso que esto, pero para el propósito de CI solo es necesario:

fastlane_version "2.50.1"
 
default_platform :ios
 
platform :ios do
 
  lane :test do
    cocoapods
    scan(scheme: "Customers_Dev")
  end
 
end

Con esta configuración nos aseguramos de que cada vez que se suba un cambio en master o se abra un pull request se ejecuten los test para garantizar que la aplicación funciona correctamente.

Conclusión

El proceso para configurar la integración continua no dista mucho de lo que se puede hacer en otros proyectos.

Aun así hay que tener en cuenta pequeños detalles como que el runner donde se quiere hacer los test tiene que tener el sistema operativo macOS, con el Xcode y sus herramientas instaladas. Además de que el runner tiene que configurarse con el executor shell.

La herramienta fastlane es opcional, con comandos de consola podrías hacer lo mismo, pero para el desarrollo lo consideramos imprescindible por lo mucho que simplifica determinados procesos.

Con la integración continua puedes quitarte un peso mental de encima. No tienes que pensar en pasar los tests antes de subirlos o hacer merge con master. Esto suele derivar a dejar de pasar los tests, lo que es peor dejar de hacerlos.

Este es un pequeño paso para llegar a realizar subidas a producción con solo un clickcommit. Con esta integración, fastlane nos permite en una sola lane pasar los tests, subir la versión, tomar las capturas de pantalla, crear la ficha con el What’s new?, compilar y subirlo todo a la AppStore de forma automática.

 

Loading Likes...

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *