Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Scripts de build e servidores de CI: você nunca mais vai querer executar qualquer tarefa de forma manual

Scripts de build e servidores de CI: você nunca mais vai querer executar qualquer tarefa de forma manual

Palestra realizada no TDC Florianópolis 2018

Ione Souza Junior

April 19, 2018
Tweet

More Decks by Ione Souza Junior

Other Decks in Technology

Transcript

  1. Scripts de build e servidores de CI Você nunca mais

    vai querer executar qualquer tarefa de forma manual Ione Souza Junior
  2. O que veremos na apresentação? → Um estudo de caso

    sobre como foi realizado a entrega de um app para diversos clientes → Discutir as dificuldades encontradas → Mostrar as soluções adotadas
  3. História do app → Era uma vez um ERP... →

    Um app foi desenvolvido integrado ao sistema → App foi publicado, os clientes começaram a usar o app, até que...
  4. História do app → Alguns clientes se demonstraram um pouco

    vaidosos → Eles queriam um app próprio, publicado com seu nome e sua logo
  5. Fork → O fork foi o começo do fim... →

    Muitos conflitos → A demanda aumentou: mais projetos, mais trabalho, menos tempo para tomar café
  6. O que realmente precisava ser feito? → Alterar drawables Android

    → Alterar resources iOS → Alterar AndroidManifest.xml → Alterar Info.plist → Alterar Entitlements.plist
  7. O que realmente precisava ser feito? → Alterar constantes -

    classe C# → Alterar styles - resource dictionary
  8. Padronizações - parte I → Criamos uma pasta para armazenar

    as imagens específicas de cada cliente
  9. Padronizações - parte I Solução └─── Core └─── Droid └───

    iOS └─── Build │ └─── script.sh │ └─── Clientes │ └─── Cliente1 │ │ └─── Droid │ │ │ └─── arquivo1.png │ │ │ └─── arquivo2.png │ │ │ │ │ └─── iOS │ │ └─── arquivo1.png │ │ └─── arquivo2.png │ │ │ └─── Cliente2 │ └─── ...
  10. Início do script #!/bin/bash SOLUTION_DIR=/diretorio/projeto CLIENTE=Cliente1 cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/Droid/Resources/drawable/*.png \ $SOLUTION_DIR/Droid/Resources/drawable/

    cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/Droid/Resources/drawable-hdpi/*.png \ $SOLUTION_DIR/Droid/Resources/drawable-hdpi/ cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/iOS/Assets.xcassets/AppIcon.appiconset/*.png \ $SOLUTION_DIR/iOS/Assets.xcassets/AppIcon.appiconset/
  11. Início do script #!/bin/bash SOLUTION_DIR=/diretorio/projeto CLIENTE=Cliente1 cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/Droid/Resources/drawable/*.png \ $SOLUTION_DIR/Droid/Resources/drawable/

    cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/Droid/Resources/drawable-hdpi/*.png \ $SOLUTION_DIR/Droid/Resources/drawable-hdpi/ cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/iOS/Assets.xcassets/AppIcon.appiconset/*.png \ $SOLUTION_DIR/iOS/Assets.xcassets/AppIcon.appiconset/ cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/iOS/Assets.xcassets/LaunchImage.launchimage/*.png \ $SOLUTION_DIR/iOS/Assets.xcassets/LaunchImage.launchimage/
  12. Início do script #!/bin/bash SOLUTION_DIR=/diretorio/projeto CLIENTE=Cliente1 cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/Droid/Resources/drawable/*.png \ $SOLUTION_DIR/Droid/Resources/drawable/

    cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/Droid/Resources/drawable-hdpi/*.png \ $SOLUTION_DIR/Droid/Resources/drawable-hdpi/ cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/iOS/Assets.xcassets/AppIcon.appiconset/*.png \ $SOLUTION_DIR/iOS/Assets.xcassets/AppIcon.appiconset/ cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/iOS/Assets.xcassets/LaunchImage.launchimage/*.png \ $SOLUTION_DIR/iOS/Assets.xcassets/LaunchImage.launchimage/ cp $SOLUTION_DIR/Build/Clientes/$CLIENTE/iOS/Resources/*.png \ $SOLUTION_DIR/iOS/Resources/
  13. O que realmente precisava ser feito? → ✅ Alterar drawables

    Android → ✅ Alterar resources iOS → Alterar AndroidManifest.xml → Alterar Info.plist → Alterar Entitlements.plist
  14. Continuação do script - AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    android:versionName="1.4" package="com.ionixjunior.app" android:versionCode="4" > <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="26" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <application android:label="Ionix App"></application> </manifest>
  15. Continuação do script - AndroidManifest.xml #!/bin/bash SOLUTION_DIR=/diretorio/projeto MANIFEST=$SOLUTION_DIR/Droid/Properties/AndroidManifest.xml VERSION_NAME=1.5 APP_NAME=Ionix

    App Corp PACKAGE=com.ionix.corp sed -i '' 's/versionName="[0-9.]*"/versionName="'$VERSION_NAME'"/' $MANIFEST sed -i '' 's/android:label="[a-zA-Zà-úÀ-Ú0-9 ]*"/android:label="'"$APP_NAME"'"/' $MANIFEST
  16. Continuação do script - AndroidManifest.xml #!/bin/bash SOLUTION_DIR=/diretorio/projeto MANIFEST=$SOLUTION_DIR/Droid/Properties/AndroidManifest.xml VERSION_NAME=1.5 APP_NAME=Ionix

    App Corp PACKAGE=com.ionix.corp sed -i '' 's/versionName="[0-9.]*"/versionName="'$VERSION_NAME'"/' $MANIFEST sed -i '' 's/android:label="[a-zA-Zà-úÀ-Ú0-9 ]*"/android:label="'"$APP_NAME"'"/' $MANIFEST sed -i '' 's/package="[a-zA-Z.]*"/package="'$PACKAGE'"/' $MANIFEST !
  17. O que realmente precisava ser feito? → ✅ Alterar drawables

    Android → ✅ Alterar resources iOS → ✅ Alterar AndroidManifest.xml → Alterar Info.plist → Alterar Entitlements.plist
  18. Continuação do script - Info.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist

    PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version="1.0"> <dict> <key>CFBundleIdentifier</key> <string>com.ionixjunior.app</string> <key>CFBundleName</key> <string>Ionix App</string> <key>CFBundleShortVersionString</key> <string>1.4</string> </dict> </plist>
  19. Continuação do script - Info.plist #!/bin/bash SOLUTION_DIR=/diretorio/projeto INFO_PLIST=$SOLUTION_DIR/iOS/Info.plist VERSION_NAME=1.5 APP_NAME=Ionix

    App Corp PACKAGE=com.ionix.corp plutil -replace CFBundleIdentifier -string $PACKAGE $INFO_PLIST plutil -replace CFBundleName -string $APP_NAME $INFO_PLIST
  20. Continuação do script - Info.plist #!/bin/bash SOLUTION_DIR=/diretorio/projeto INFO_PLIST=$SOLUTION_DIR/iOS/Info.plist VERSION_NAME=1.5 APP_NAME=Ionix

    App Corp PACKAGE=com.ionix.corp plutil -replace CFBundleIdentifier -string $PACKAGE $INFO_PLIST plutil -replace CFBundleName -string $APP_NAME $INFO_PLIST plutil -replace CFBundleShortVersionString -string $VERSION_NAME $INFO_PLIST
  21. O que realmente precisava ser feito? → ✅ Alterar drawables

    Android → ✅ Alterar resources iOS → ✅ Alterar AndroidManifest.xml → ✅ Alterar Info.plist → Alterar Entitlements.plist
  22. Continuação do script - Entitlements.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist

    PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" > <plist version="1.0"> <dict> <key>aps-environment</key> <string>development</string> </dict> </plist>
  23. O que realmente precisava ser feito? → ✅ Alterar drawables

    Android → ✅ Alterar resources iOS → ✅ Alterar AndroidManifest.xml → ✅ Alterar Info.plist → ✅ Alterar Entitlements.plist Mas ainda não acabou...
  24. O que realmente precisava ser feito? → Alterar constantes -

    classe C# → Alterar styles - resource dictionary
  25. Continuação do script - constantes public static class AppConfig {

    public const string ApiUrl = "https://homol.app.com"; }
  26. O que realmente precisava ser feito? → ✅ Alterar constantes

    - classe C# → Alterar styles - resource dictionary
  27. Continuação do script - styles - resource dictionary <?xml version="1.0"

    encoding="utf-8"?> <Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Core.App" > <Application.Resources> <ResourceDictionary> <Color x:Key="PrimaryColor">#3F51B5</Color> <Color x:Key="SecondaryColor">#F90000</Color> </ResourceDictionary> </Application.Resources> </Application>
  28. Continuação do script - styles - resource dictionary #!/bin/bash SOLUTION_DIR=/diretorio/projeto

    APP_XAML=$SOLUTION_DIR/Core/App.xaml PRIMARY_COLOR=Green SECONDARY_COLOR=#0000FF sed -i '' 's/"PrimaryColor">[a-zA-Z0-9#]*</"PrimaryColor">'$PRIMARY_COLOR'</' $APP_XAML sed -i '' 's/"SecondaryColor">[a-zA-Z0-9#]*</"SecondaryColor">'$SECONDARY_COLOR'</' $APP_XAML
  29. O que realmente precisava ser feito? → ✅ Alterar constantes

    - classe C# → ✅ Alterar styles - resource dictionary !
  30. Padronizações - parte II → Criamos um arquivo para armazenar

    as variáveis do build # ./Build/app.ini CLIENTE = Cliente1 API_URL = https://prod.app.com VERSION_NAME = 1.5 APP_NAME = Ionix App Corp PACKAGE = com.ionix.corp
  31. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=
  32. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$()
  33. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI) # Vai mostrar o conteúdo de todo o arquivo...
  34. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI | grep "CLIENTE = ") # CLIENTE = Cliente1
  35. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI | grep "CLIENTE = " | awk) #
  36. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI | grep "CLIENTE = " | awk '{print $0}') # CLIENTE = Cliente1
  37. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI | grep "CLIENTE = " | awk '{print $1}') # CLIENTE
  38. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI | grep "CLIENTE = " | awk '{print $2}') # =
  39. Padronizações - parte II → No script principal, carregamos estas

    variáveis... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini CLIENTE=$(cat $APP_INI | grep "CLIENTE = " | awk '{print $3}') # Cliente1
  40. Padronizações - parte II → Outro exemplo... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini

    APP_NAME=$(cat $APP_INI | grep "APP_NAME = " | awk '{print $3}') # Ionix Está faltando coisa aí...
  41. Padronizações - parte II → Vamos revisar... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini

    APP_NAME=$(cat $APP_INI | grep "APP_NAME = " | awk '{print $0}') # APP_NAME = Ionix App Corp
  42. Padronizações - parte II → Vamos revisar... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini

    APP_NAME=$(cat $APP_INI | grep "APP_NAME = " | awk '{print $3 " " $4 " " $5}') # Ionix App Corp
  43. Padronizações - parte II → Como deixar isso dinâmico?? ...

    awk '{print $3 " " $4 " " $5}' # Ionix App Corp
  44. Padronizações - parte II → Como deixar isso dinâmico?? ...

    awk '{ {out=$3} }' # APP_NAME = Ionix App Corp # out = Ionix
  45. Padronizações - parte II → Como deixar isso dinâmico?? ...

    awk '{ {out=$3}; for (i=4; i <= NF; i++) { } }' # APP_NAME = Ionix App Corp # out = Ionix # NF = number of fields = 5
  46. Padronizações - parte II → Como deixar isso dinâmico?? ...

    awk '{ {out=$3}; for (i=4; i <= NF; i++) { out=out" "$i } }' # APP_NAME = Ionix App Corp # out = Ionix # out = Ionix App # out = Ionix App Corp
  47. Padronizações - parte II → Como deixar isso dinâmico?? ...

    awk '{{out=$3; for(i=4; i<=NF; i++) { out=out" "$i }; print out}}' # Ionix App Corp !
  48. Padronizações - parte II → Outro exemplo... #!/bin/bash SOLUTION_DIR=/diretorio/projeto APP_INI=$SOLUTION_DIR/Build/app.ini

    MANIFEST = $SOLUTION_DIR/Droid/Properties/AndroidManifest.xml INFO_PLIST = $SOLUTION_DIR/iOS/Info.plist ENTITLEMENTS_PLIST = $SOLUTION_DIR/iOS/Entitlements.plist APP_CONFIG = $SOLUTION_DIR/Core/AppConfig.cs CLIENTE = $(cat $APP_INI | grep "CLIENTE = " | awk '{print $3}') API_URL = $(cat $APP_INI | grep "API_URL = " | awk '{print $3}') VERSION_NAME = $(cat $APP_INI | grep "VERSION_NAME = " | awk '{print $3}') APP_NAME = $(cat $APP_INI | grep "APP_NAME = " | awk '{{out=$3; for(i=4;i<=NF;i++){out=out" "$i}; print out}}') PACKAGE = $(cat $APP_INI | grep "PACKAGE = " | awk '{print $3}')
  49. Visual Studio App Center → Muito fácil de usar →

    Ideal para quem está iniciando no mundo DevOps → Poucos cliques e os problemas estão resolvidos
  50. Visual Studio App Center → Build → UI Test →

    Distribuir para grupos de testes e lojas → Diagnóstico de erros → Analytics → Push
  51. Como nosso script lê as variáveis do App Center? →

    Customizações precisaram ser feitas...
  52. Padronizações - parte III → O app.ini não deve ser

    versionado → Então podemos verificar se ele existe no script...
  53. Padronizações - parte III #!/bin/bash # Script depois :) SOLUTION_DIR=$1

    APP_INI=$SOLUTION_DIR/Build/app.ini if [ -e "$APP_INI" ] then CLIENTE=$(cat $APP_INI | grep "CLIENTE = " | awk '{print $3}') else # Não precisa fazer nada, as variáveis já foram setadas :) fi
  54. Padronizações - parte III → Ainda falta deixar dinâmico o

    $SOLUTION_DIR... → O App Center também tem suas próprias variáveis de ambiente
  55. Padronizações - parte III #!/bin/bash # Script depois :) if

    [ -n "$1" ] then SOLUTION_DIR=$1 else SOLUTION_DIR=$APPCENTER_SOURCE_DIRECTORY fi
  56. Padronizações - parte III → O App Center não vai

    achar esse script sozinho → É necessário criar o script post-clone / pre- build / post-build
  57. Padronizações - parte III Solução └─── Build └─── Droid │

    └─── appcenter-post-clone.sh │ └─── appcenter-pre-build.sh │ └─── appcenter-post-build.sh └─── iOS └─── appcenter-post-clone.sh └─── appcenter-pre-build.sh └─── appcenter-post-build.sh
  58. Sabia que você pode contribuir? → Projeto App Center Build

    Scripts Examples → https://github.com/Microsoft/appcenter-build- scripts-examples
  59. O processo ainda não acaba O que vimos é o

    mais complicado O restante é bem simples
  60. Publicando o app na loja → Conectar o projeto a

    loja → Google Play → Apple Store
  61. Publicando o app na loja → Envie o APK ou

    IPA → Adicione o release notes → Ser feliz !
  62. Publicando o app na loja → Limitações no iOS: somente

    é possível enviar release notes em inglês !
  63. Na palestra de hoje vimos que... → É possível compilar

    um app de forma dinâmica → Você vai se encher de repetir as mesmas tarefas → Precisamos fazer a máquina trabalhar mais → Automatização é tudo