Playrix CI / CD: comment nous construisons et testons nos jeux

L'équipe devrait se concentrer sur la création de jeux magnifiques et réussis, pour tout le reste, il y a CI.

Où appliquons-nous CI? Quelles approches et quels concepts utilisons-nous? Pourquoi construire et tester des builds? L'histoire détaillée de CI et de son organisation dans Playrix dessinera un cours magistral. Sous la coupe - une brève pression et quelques accents.


Salut

Tout d'abord, un petit échauffement: qu'est-ce que l'intégration continue? Si l'équipe utilise le référentiel et collecte les builds de nuit - est-ce déjà CI? Quelle est la différence entre le déploiement continu et la livraison? C'est presque sans importance. Détails - pour un cercle restreint de spécialistes. Si vous souhaitez impliquer l'ensemble de l'entreprise dans un processus, trouvez-en un nom simple et bon. Dans Playrix, nous appelons toutes ces approches CI. C'est une telle marque locale et un logo sympa:

Idée


L'IC n'est pas un objectif, c'est un outil. Il doit y avoir un objectif que vous souhaitez atteindre avec l'intégration continue, un problème qui doit être résolu. Il arrive que les processus de développement et de sortie d'une équipe soient construits sur le principe «prié et en production». Parfois, c'est justifié, mais rarement.

Nous avons formulé notre objectif comme suit: minimiser la probabilité de problèmes d'intégration, minimiser les ressources nécessaires pour corriger les erreurs trouvées, réduire le temps des équipes de développement de projet pour soutenir et soutenir les processus CI.
CI est une automatisation des processus de construction, des tests de code et sa livraison à divers environnements, l'automatisation des processus de développement de routine, l'intégration mutuelle des services que nous utilisons tous.

L'idée est dans un système qui collecte automatiquement tout automatiquement, le fait souvent, teste et fournit une génération, et signale également de manière ciblée un rapport pratique en cas de problème.

Où appliquons-nous CI?


  • Moteur et utilitaires,
  • nos jeux pour toutes les plateformes,
  • code serveur
  • analytique, services marketing, automatisation divers, services CI,
  • l'infrastructure.

C'est partout ou presque partout.

L'assemblage et le test des conteneurs, le déploiement automatique dans les mises à jour Test, Staging et Prod, Rolling et Canary - tout cela que nous avons et plus applicable aux services et applications Web. Aujourd'hui, nous allons nous concentrer sur CI pour les jeux: construire des builds, les tester et les livrer.

Paradigmes


Pour atteindre les objectifs énoncés ci-dessus, vous devez résoudre plusieurs problèmes. Vous trouverez ci-dessous un plan que nous suivons lorsque nous automatisons un processus en cours de développement, par exemple, l'assemblage d'un client de jeu mobile. Il est très pratique d'avoir une liste de questions auxquelles vous pouvez résoudre tout problème qui relève de l'équipe CI.

  • La documentation


Les instructions d'assemblage sont une documentation, une description de notre processus automatisé. Une telle documentation est souvent à la tête des programmeurs. Si une équipe a un super-spécialiste dans la construction de builds et sans lui, personne ne peut plus construire une build rapide et sans erreur - il est temps de changer quelque chose, il n'y aura pas de soumission.

Eh bien, si une telle documentation est encadrée sous la forme d'un script: j'ai entré une ligne de commande et un ensemble de paramètres sur une machine avec un environnement préparé - j'ai eu une build.

La meilleure documentation de processus est le code cat . Même si, pour une raison quelconque, vous devez répéter l'opération manuellement, vous pouvez toujours le faire en l'examinant.
  • Journalisation


Le journal de build vous permet de toujours dire avec certitude: qui, quand, à partir de quel commit, et avec quel résultat telle ou telle build a été collectée. Hier, la construction était sur le point, mais aujourd'hui, ce n'était pas le cas. Nous regardons dans le journal, nous trouvons la première génération de disquette, nous voyons la liste des commits qui y sont arrivés - profit.

Le magazine est encore plus utile lorsqu'il s'agit, par exemple, de code serveur. S'il n'y a aucune information sur qui a mis à jour le prod et quand, alors dans le cas général, on ne sait pas quel code y travaille actuellement. Et parfois, c'est très important, très.

Vous pouvez conserver un tel journal dans le grand livre, de préférence dans un tableau ou un wiki. La liste des builds dans le système CI est inestimable.


  • La sécurité


Quand il s'agit de construire une build et de déployer sur une sorte d'environnement, la question se pose toujours: où stocker les mots de passe de connexion / accès? Ils ont généralement besoin de beaucoup: au référentiel pour télécharger les données source, au stockage de fichiers pour remplir les ressources du jeu, à HockeyApp pour envoyer des personnages, au serveur pour mettre à jour le code, etc.

Il arrive que tous les accès nécessaires soient stockés dans le référentiel. Il y a une hypothèse que ce n'est pas très bon. Souvent, vous pouvez voir le champ "enter password", disons, à Jenkins, où l'auteur de la construction entre les caractères cachés.

Se souvenir de tous les mots de passe par cœur est une bonne compétence. Notre serveur CI lui-même reçoit les accès nécessaires en fonction de l'assemblage. Habituellement, ce sont des jetons de courte durée qui sont générés au début de la génération et donnent des droits minimaux exactement là où nous déployons quelque chose ou où nous lisons quelque chose.

La gestion centralisée de l'assemblage et du déploiement vous permet de résoudre le problème de la différenciation des droits d'accès à l'infrastructure. Pourquoi donner à quelqu'un l'accès au serveur si vous ne pouvez donner accès qu'à l'assembly de la build correspondante qui effectue l'opération nécessaire sur ce serveur? Et comme il y a une version, nous avons de la documentation et de la journalisation, eh bien, vous comprenez.

  • Traçabilité


Il y a généralement beaucoup de traces laissées pendant la construction. Non, pas comme ça: lors de la construction, il faut laisser autant de traces que possible. Dans le référentiel, dans le tracker de tâches, dans le système de distribution de build. Partout, où que vous rencontriez un build, il devrait y avoir des traces qui vous conduisent à compléter les informations le concernant.

Ces traces n'ont pas besoin d'être balayées, au contraire, elles doivent être soigneusement laissées et soigneusement conservées. De plus, je vais vous en dire plus à ce sujet, mais nous devons d'abord récupérer notre build. Allons-y.

Crochets de pré-validation


Encore une fois, l'idée est un système qui recueille, teste et rapporte tout. Mais pourquoi construire un build si vous ne pouvez pas le construire?

Tous les développeurs de nos jeux ont installé des crochets de pré-validation, c'est-à-dire un ensemble de vérifications qui sont effectuées lors de la tentative de validation de quelque chose. Les vérifications ne s'exécutent que pour les fichiers modifiés, mais nous avons implémenté un système de recherche interdépendant très astucieux pour vérifier également tout le contenu associé. C'est-à-dire si l'artiste a ajouté une texture, les crochets vérifieront qu'ils n'ont pas oublié de l'enregistrer là où c'est nécessaire et ne l'ont jamais scellée.

Il s'avère que les crochets attrapent une partie importante des petites erreurs. Ils économisent les ressources du système de construction et aident le développeur à résoudre rapidement le problème: il voit un message qui explique en détail ce qui n'a pas fonctionné. Et il n'a pas besoin de basculer entre les tâches: il vient littéralement d'apporter des modifications et est en contexte. Le temps de correction des erreurs est minime.

Nous l'avons tellement aimé que nous avons même créé un système qui vérifie si des hooks ont été faits pour une validation qui est entrée dans le référentiel. Sinon, l'auteur d'une telle validation recevra automatiquement une tâche lui demandant de les configurer et des instructions détaillées sur la façon de procéder.

Les crochets sont standardisés pour tous les projets. Le nombre de tests personnalisés est minime. Il y a une personnalisation pratique, y compris en fonction de l'utilisateur qui exécute: c'est très pratique pour tester les tests.

Construire


Pour voir le problème dans la version le plus tôt possible, vous devez collecter et tester ces versions aussi souvent que possible. Les clients de nos jeux se réunissent pour toutes les plateformes, pour chaque commit, pour tous les projets. Il y a probablement quelques exceptions, mais pas beaucoup.

En règle générale, un client, en particulier un mobile, a plusieurs versions différentes: avec et sans triche, signé différemment, etc. Pour chaque commit, nous collectons des builds "réguliers", que les développeurs et les testeurs utilisent constamment.

Il y a des builds qui sont utilisés très rarement, par exemple, le store ios build - une seule fois dans une soumission, c'est-à-dire environ une fois par mois. Cependant, nous pensons que toutes les versions doivent être collectées régulièrement. Si un problème survient avec ce type d'assemblage, côté développement ou infrastructure, l'équipe du projet en sera informée non pas le jour de la livraison, mais bien plus tôt, et sera en mesure de répondre et de résoudre le problème à l'avance.

En conséquence, nous avons une règle simple: toute version est lancée au moins une fois par jour. L'équipe de développement du projet découvre la présence de tout problème sur n'importe quelle plate-forme dans le pire des cas le lendemain matin, après l'apparition de ce problème dans le référentiel.

Une telle fréquence d'assemblages et de tests nécessite une approche particulière pour optimiser leur temps d'exécution.

  • Toutes les versions client régulières sont incrémentielles.
  • L'emballage des atlas et la préparation des ressources sont également progressifs.
  • Les assemblages sont granulaires: certaines étapes sont dans des configurations de construction distinctes - cela vous permet de les exécuter en parallèle, ainsi que de réutiliser les résultats intermédiaires.

Il s'agit d'une capture d'écran presque complète de la chaîne de builds et de tests pour WildScapes. Le plein n'a pas pu être fait: il est environ deux fois plus grand.


Tests statiques


Après l'assemblage, des tests statiques sont effectués: nous prenons le dossier avec la construction et effectuons un ensemble de vérifications de tout le contenu qui s'y trouve. Le code est également du contenu, donc son analyse statique (cppcheck + PVS-Studio) est également ici.

Sur un habr il y avait un matériel détaillé sur la façon dont nous avons implémenté les tests statiques, je le recommande. Je souligne seulement que les tests statiques après la construction et dans les hooks de pré-validation sont exécutés par le même code. Cela simplifie considérablement la prise en charge du système.

Tests d'exécution


Si la génération de tests statiques a réussi, vous pouvez continuer et essayer d'exécuter la génération assemblée. Nous testons des builds sur toutes les plateformes sauf UWP, c'est-à-dire Windows, MacOs, iOS, Android. UWP - le sera aussi, mais un peu plus tard.

Pourquoi tester les versions de bureau si elles ne semblent nécessaires qu'en développement? La réponse à la question est: c'est mauvais si un artiste ou un level designer obtient une version qui plante au démarrage pour une raison ridicule. Par conséquent, Smoke-Test, l'ensemble minimal de vérifications pour l'exécutabilité et le gameplay de base, est effectué pour toutes les plateformes.

Tout ce qui a été écrit ci-dessus sur les builds est également vrai pour les tests sur les appareils - au moins une fois par jour. À quelques exceptions près: il y a de très longs tests qui n'ont pas le temps de se terminer en une journée.

Des tests de fumée sont exécutés pour chaque commit. La réussite des vérifications de base est une condition préalable pour que la version pénètre dans le système de distribution. Habituellement, cela n'a aucun sens de donner à quelqu'un l'accès à une version qui ne fonctionne évidemment pas. Ici, vous pouvez vous opposer et proposer des exceptions. Les projets ont une solution de contournement pour donner accès à une version non fonctionnelle, mais ils l'utilisent à peine.

Quels sont les autres tests:

  • Benchmark: nous vérifions les performances sur le FPS et la mémoire dans diverses situations et sur tous les appareils.
  • Tests unitaires Match-3: chaque élément et chaque mécanique sont testés individuellement et dans toutes les combinaisons d'interaction.
  • Le passage du jeu entier du début à la fin.
  • Divers tests de régression, par exemple, des tests de localisation, ou que toutes les fenêtres de l'interface utilisateur s'ouvrent correctement, ou que les scènes fishdom dans Fishdom se lisent sans erreurs.
  • Tout de même, mais avec AddressSanitizer .
  • Tests de compatibilité pour les versions de jeu: prenez le fichier de sauvegarde de l'utilisateur des versions précédentes, ouvrez-le dans la nouvelle version et assurez-vous que tout va bien.
  • Divers tests personnalisés pertinents pour la mécanique d'un projet particulier.

Pour exécuter les tests, nous utilisons notre propre banc d'essai d'appareils iOS et Android. Cela nous permet de lancer de manière flexible les versions dont nous avons besoin sur les appareils, d'interagir avec l'appareil à partir du code. Nous avons un contrôle total, un niveau de fiabilité compréhensible, nous savons quels problèmes nous pouvons rencontrer et combien de temps il faudra pour les résoudre. Aucun des services cloud qui fournissent des appareils de test n'offre un tel confort.

CHATS


Les tests répertoriés ci-dessus sont implémentés dans le code du projet. Cela permet en théorie de faire un test de toute complexité, mais nécessite des efforts et des ressources de la part du développement du projet pour implémenter et supporter ces tests. Ces ressources ne sont souvent pas disponibles et il est difficile et inutile de tester plusieurs régressions à la main. Je voulais vraiment que les testeurs eux-mêmes fassent l'automatisation des tests. Et nous avons trouvé un cadre pour eux - le système de test d'automatisation continue, ou CATS.

Quelle est l'idée: permettre aux auteurs de scripts de test d'interagir avec l'application de jeu, sans se soucier de la façon dont tout cela fonctionne. Nous écrivons des scripts en python primitif, nous accédons à l'application via un ensemble d'abstractions. Par exemple: "Homescapes, ouvrez-moi une fenêtre d'achat et achetez tel ou tel produit." Vérifiez le résultat, bingo.

L'implémentation complète des commandes de script est cachée derrière un ensemble d'abstractions. L'API qui implémente l'interaction avec l'application vous permet d'effectuer n'importe quelle action de plusieurs manières:

  • envoyer une requête http au serveur, qui est intégré au moteur de jeu, avec une sorte de commande. Cette commande est traitée par le code du jeu. Habituellement, c'est une sorte de triche, elle peut être arbitrairement simple ou complexe. Par exemple, "donnez-moi les coordonnées du centre du bouton avec l'identifiant spécifié." Ou "passez-moi le jeu d'ici au niveau avec le numéro indiqué."
  • Nous pouvons ouvrir une fenêtre à travers la triche ou découvrir les coordonnées du bouton par lequel cette fenêtre s'ouvre, nous pouvons émuler en cliquant sur ce bouton, nous pouvons effectuer un clic virtuel dessus.
  • Enfin, nous pouvons effectuer un «vrai» clic sur les coordonnées spécifiées comme si cela avait été fait avec un doigt sur l'écran.

Cette dernière méthode ouvre la porte à l'imagination des testeurs qui souhaitent souvent tester des builds «combat», où il n'y a pas de triche. Le maintien de tels scénarios est plus difficile, mais une «construction de combat» est une construction de «combat».


Il s'est avéré très pratique de travailler avec les coordonnées du centre du bouton: les coordonnées changent parfois, mais les identifiants des boutons sont rares. Cela a conduit à une autre propriété importante du système: la possibilité d'écrire un script de test pour toutes les plates-formes et toutes les résolutions d'écran.

Livraison, rapports et traces


Avec la livraison, tout s'est avéré assez simple: nous utilisons un seul stockage partagé pour construire des artefacts et pour le stockage dans le système de distribution. Le «chargement» de la build revient à invoquer une paire de requêtes vers l'api du service de distribution de build, essentiellement l'enregistrement. De cette façon, nous avons économisé un peu de temps sur le pompage des constructions et de l'argent pour leur stockage.

Rappelez-vous, vous avez parlé de minimiser les ressources nécessaires pour corriger les erreurs trouvées dans les versions? Rapports et pistes - à peu près ceci:

  • Signaler un problème détecté est une tâche dans Asana. Il est facile à contrôler, à l'assigner au bon développeur, à le transférer à l'équipe CI en cas de problème dans l'infrastructure.
  • Nous collectons des builds pour chaque commit. Nous connaissons l'auteur de ce commit, donc lui seul verra cette tâche. Nous gagnons donc du temps pour les autres développeurs: ils n'ont pas besoin d'être distraits par des problèmes avec lesquels ils n'ont rien à voir et aident à les résoudre, ce qu'ils ne seront probablement pas en mesure de résoudre.
  • Si vous créez une build à partir du prochain commit, il est très probable qu'elle soit toujours rompue. Il y aura un commentaire dans la tâche: "La construction est toujours cassée", l'auteur du nouveau commit ne verra pas la tâche et ne perdra pas de temps sur le problème de quelqu'un d'autre.
  • Nous envoyons des rapports à Slack. Nécessairement - personnellement à celui qui a "cassé" la construction, et si le projet le veut - à une chaîne spéciale ou à tout employé de Playrix. Tout est aussi flexible que possible.

Des traces sont nécessaires pour que partout il y ait des informations complètes sur la construction et les modifications à partir desquelles elle a été collectée. Afin de ne rien chercher, pour que tout soit à portée de main et qu'il ne soit pas nécessaire de passer du temps à rechercher les détails qui sont souvent nécessaires lors de la recherche d'un problème.

  • Le rapport contient un lien vers la génération, vers le journal de génération, le texte de l'erreur de compilation trouvée et les noms des tests inversés. Souvent, un programmeur, après avoir reçu une tâche de rapport, peut immédiatement corriger l'erreur: le nom du fichier, la ligne et le texte de l'erreur figurent dans le rapport.
  • Le message dans Slack contient tout de même + un lien vers la tâche dans Asana.
  • Dans Teamcity, un lien vers une tâche. L'ingénieur de construction peut immédiatement se lancer dans la tâche, en un clic, vous n'avez rien à chercher.
  • Dans github - statut avec un lien vers la build, dans le commentaire du commit - un lien vers la tâche pour laquelle cette build a été faite. Dans la tâche - un commentaire avec un lien vers la validation.
  • Dans le service de distribution de build: lien vers la build, lien vers la validation.

Il n'y a rien à retenir, mais vous avez compris l'idée: des liens vers tout, partout. Cela accélère considérablement l'étude de toute situation incompréhensible.

Ferme


Nous collectons et testons des versions pour toutes les plateformes. Pour cela, nous avons besoin de nombreux agents différents. Leur suivi et leur maintenance manuellement sont longs et difficiles. Tous les agents sont préparés automatiquement. Nous utilisons Packer et Ansible.

Tous les journaux de tous les agents, Teamcity, tous les services qui sont autour, nous les enregistrons (dans notre cas - dans ELK). Tous les services, traitant une build, ajoutent le numéro de cette build à chaque ligne de journaux. Nous pouvons voir dans une seule demande l'ensemble du cycle de vie de la build depuis son apparition dans la file d'attente jusqu'à la fin de l'envoi de tous les rapports.

Nous avons mis en place notre propre mécanisme d'optimisation de file d'attente. Celui de Teamcity ne fonctionne pas très bien avec nos chiffres. En parlant de chiffres:

  • Nous collectons environ 5 000 versions chaque jour. Cela représente environ 500 heures de travail.
  • La trois millionième construction remonte à un mois.
  • Nous avons plus de 50 serveurs de build dans 10 endroits différents.
  • Plus de 40 appareils mobiles sur un banc d'essai.
  • Exactement 1 serveur Teamcity.


CI en tant que service


Playrix CI est un service. Il y a beaucoup de projets, beaucoup d'idées aussi.

Nous optimisons le temps entre la mise en file d'attente du build et la fin de son exécution, car c'est exactement ce que le «temps de build» est considéré par l'utilisateur du service, le développeur. Cela nous permet de rechercher et de trouver un équilibre entre le temps de génération et le temps passé dans la file d'attente. Il semble logique qu'avec la croissance de l'entreprise et le nombre de projets, la ferme de construction qui recueille ces projets grandira également. Mais grâce aux optimisations, le taux de croissance de la ferme est loin derrière le taux de croissance de l'entreprise.

Toute optimisation commence par la surveillance, avec une collecte méthodique de statistiques. Nous collectons beaucoup de statistiques et nous savons absolument tout sur nos builds. Mais en plus du volume de la batterie de build, il existe également une équipe qui prend en charge le système CI et fait en sorte que personne n'ait besoin de penser à l'origine des builds.

L'optimisation des processus dans cette équipe est également un processus intéressant et divertissant.Par exemple, nous écrivons des tests pour définir des configurations de builds, car il y a beaucoup de ces configurations, sans tests similaires, il n'est pas facile de trouver tous les endroits qui doivent être modifiés. Pour presque tous les changements, nous écrivons d'abord un test, puis nous les faisons, c'est-à-dire, en fait, nous avons TDD. Il existe de nombreux processus liés aux tâches, à la gestion des incidents et à la planification du flux des tâches entrantes.

Les développeurs doivent se concentrer sur la création de jeux géniaux et réussis sans se soucier de l'origine des builds. Pour cela, Playrix a un CI. Il doit y avoir un objectif que vous souhaitez atteindre avec l'intégration continue, un problème qui doit être résolu. Il est important de ne pas trouver de problème, à savoir de le trouver. Et quand vous la trouverez, souvenez-vous de notre expérience et faites mieux. Et rappelez-vous:

CI ne dort jamais A bientôt
!

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


All Articles