Playrix CI / CD: como construímos e testamos nossos jogos

A equipe deve se concentrar na criação de jogos bonitos e bem-sucedidos, para todo o resto existe CI.

Onde aplicamos o IC? Quais abordagens e conceitos usamos? Por que construir e testar compilações? A história detalhada sobre a CI e como ela é organizada no Playrix fará um curso de palestras. Sob o corte - um breve aperto e alguns sotaques.


Oi

Primeiro, um pouco de aquecimento: o que é integração contínua? Se a equipe usar o repositório e coletar compilações noturnas - esse IC já está? Qual é a diferença entre Implantação Contínua e Entrega? É quase sem importância. Detalhes - para um círculo estreito de especialistas. Se você quiser envolver toda a empresa em algum processo, crie um nome simples e bom para ela. No Playrix, chamamos todas essas abordagens de CI. Essa é uma marca local e um logotipo legal:

Idéia


O IC não é uma meta, é uma ferramenta. Deve haver uma meta que você deseja alcançar com a Integração Contínua, um problema que precisa ser resolvido. Acontece que os processos de desenvolvimento e liberação em uma equipe são construídos com o princípio de "oração e produção". Às vezes é justificado, mas com pouca frequência.

Formulamos nosso objetivo da seguinte maneira: minimizar a probabilidade de problemas de integração, minimizar os recursos necessários para corrigir os erros encontrados, reduzir o tempo das equipes de desenvolvimento de projetos para apoiar e apoiar os processos de IC.
O CI é a automação de processos de construção, teste de código e sua entrega a vários ambientes, automação de processos de desenvolvimento de rotina, integração mútua de serviços que todos usamos.

A idéia está em um sistema que coleta tudo automaticamente, faz isso com frequência, testa e entrega uma compilação e também direciona um relatório conveniente, ponto por ponto, se algo der errado.

Onde aplicamos o IC?


  • Motor e utilitários,
  • nossos jogos para todas as plataformas,
  • código do servidor
  • análise, serviços de marketing, automação diversa, serviços de CI,
  • a infraestrutura.

Isso está em todo lugar ou quase em todo lugar.

Montagem e teste de contêineres, implantações automáticas nas atualizações de teste, preparo e produção, laminação e canárias - tudo isso que temos e mais aplicável a serviços e aplicativos da web. Hoje vamos nos concentrar no CI para jogos: criar builds, testá-los e entregá-los.

Paradigmas


Para atingir os objetivos mencionados acima, você precisa resolver vários problemas. Abaixo está um plano que estamos seguindo quando automatizamos algum processo em desenvolvimento, por exemplo, a montagem de um cliente de jogos para dispositivos móveis. É muito conveniente ter uma lista de perguntas, respondendo as quais, você pode resolver qualquer problema que caia na equipe de IC.

  • Documentação


Instruções de montagem de construção são documentação, uma descrição do nosso processo automatizado. Muitas vezes, essa documentação está na cabeça dos programadores. Se uma equipe tiver um superespecialista na construção de compilações e sem ela ninguém mais poderá criar uma compilação rápida e sem erros - é hora de mudar alguma coisa, não haverá um envio.

Bem, se essa documentação estiver enquadrada na forma de um script: entrei em uma linha de comando e em um conjunto de parâmetros em uma máquina com um ambiente preparado - obtive uma compilação.

A melhor documentação do processo é o código do gato . Mesmo que, por algum motivo, você precise repetir a operação manualmente, sempre poderá fazê-lo olhando para ela.
  • Registo


O log de compilação permite que você sempre diga com certeza: quem, quando, de qual confirmação e com que resultado essa ou aquela compilação foi coletada. Ontem a construção estava prestes a acontecer, mas hoje não estava. Nós olhamos no log, encontramos a primeira compilação de disquete, vemos a lista de confirmações que chegaram lá - lucro.

A revista é ainda mais útil quando se trata, por exemplo, de código de servidor. Se não houver informações sobre quem atualizou o produto e quando, no caso geral, não se sabe qual código está funcionando no momento. E às vezes é muito importante, muito.

Você pode manter esse diário no livro, de preferência em uma tabela ou wiki. A lista de compilações no sistema de IC não tem preço.


  • Segurança


Quando se trata de criar uma construção e implantar em algum tipo de ambiente, sempre surge a pergunta: onde armazenar senhas de login / acesso? Eles geralmente precisam de muito: no repositório para baixar os dados de origem, no armazenamento de arquivos para preencher os recursos do jogo, no HockeyApp para enviar caracteres, no servidor para atualizar o código etc.

Acontece que todos os acessos necessários são armazenados no repositório. Existe uma hipótese de que isso não seja muito bom. Geralmente, você pode ver o campo "digitar a senha", digamos, em Jenkins, onde o autor da compilação digita os caracteres ocultos.

Lembrar todas as senhas de cor é uma boa habilidade. Nosso servidor de IC recebe os acessos necessários, dependendo da montagem. Geralmente, esses tokens de vida curta são gerados no início da construção e fornecem direitos mínimos exatamente onde implantamos algo ou de onde lemos algo.

O gerenciamento centralizado da montagem e implantação permite resolver o problema de diferenciar os direitos de acesso à infraestrutura. Por que dar a alguém acesso ao servidor se você só pode dar acesso ao conjunto da compilação correspondente que faz a operação necessária nesse servidor? E como existe uma compilação, temos documentação e diário, bem, você entende.

  • Rastreabilidade


Geralmente, existem muitos traços deixados durante o tempo de compilação. Não, não é assim: durante a compilação, é necessário deixar o maior número possível de traços. No repositório, no rastreador de tarefas, no sistema de distribuição de construção. Em todos os lugares, onde quer que você encontre uma construção, deve haver rastros que o levem a concluir informações sobre ela.

Esses traços não precisam ser varridos, pelo contrário, devem ser cuidadosamente deixados e cuidadosamente preservados. Além disso, vou falar mais sobre isso, mas primeiro precisamos coletar nossa compilação. Vamos lá

Ganchos de pré-confirmação


Novamente, a ideia é um sistema que coleta, testa e relata tudo. Mas por que construir uma construção se você não pode construí-la?

Todos os desenvolvedores de nossos jogos têm ganchos de pré-confirmação instalados, ou seja, um conjunto de verificações que são executadas ao tentar confirmar algo. As verificações são executadas apenas para arquivos modificados, mas implementamos um sistema de pesquisa de dependência cruzada muito esperto para verificar também todo o conteúdo relacionado. I.e. se o artista adicionou uma textura, os ganchos verificarão se eles não se esqueceram de registrá-la sempre que necessário e nunca a selaram.

Acontece que os ganchos capturam uma parte significativa de pequenos erros. Eles economizam os recursos do sistema de compilação e ajudam o desenvolvedor a resolver rapidamente o problema: ele vê uma mensagem que diz em detalhes o que deu errado. E ele não precisa alternar entre tarefas: ele literalmente apenas fez alterações e está no contexto. O tempo de correção de erros é mínimo.

Gostamos tanto que até criamos um sistema que verifica se ganchos foram feitos para um commit que entrou no repositório. Caso contrário, o autor dessa confirmação receberá automaticamente uma tarefa solicitando a configuração e instruções detalhadas sobre como fazer isso.

Os ganchos são padronizados para todos os projetos. O número de testes personalizados é mínimo. Existe uma personalização conveniente, incluindo dependendo do usuário que está executando: isso é muito conveniente para testar testes.

Construir


Para ver o problema na compilação o mais cedo possível, você precisa coletar e testar essas compilações o mais rápido possível. Os clientes de nossos jogos se reúnem para todas as plataformas, para cada confirmação, para todos os projetos. Provavelmente existem algumas exceções, mas não muitas.

Normalmente, um cliente, especialmente um móvel, tem várias versões diferentes: com e sem truques, com assinatura diferente etc. Para cada confirmação, coletamos compilações "regulares", que desenvolvedores e testadores usam constantemente.

Há builds usados ​​muito raramente, por exemplo, a loja ios build - apenas uma vez em um envio, ou seja, cerca de uma vez por mês. No entanto, acreditamos que todas as compilações devem ser coletadas regularmente. Se ocorrer um problema com esse tipo de montagem, no lado do desenvolvimento ou da infraestrutura, a equipe do projeto saberá sobre isso não no dia em que a compilação for entregue, mas muito antes, e poderá responder e corrigir o problema com antecedência.

Como resultado, temos uma regra simples: qualquer build é iniciada pelo menos uma vez por dia. A equipe de desenvolvimento do projeto descobre a presença de problemas em qualquer plataforma, no pior dos casos, na manhã seguinte, depois que esse problema apareceu no repositório.

Essa frequência de montagens e testes requer uma abordagem especial para otimizar o tempo de execução.

  • Todas as compilações regulares de clientes são incrementais.
  • Atlas de embalagem e preparação de recursos também são incrementais.
  • As montagens são granulares: algumas das etapas estão em configurações de compilação separadas - isso permite executá-las em paralelo e reutilizar resultados intermediários.

Esta é uma captura de tela quase completa da cadeia de compilações e testes do WildScapes. O total não pôde ser feito: é duas vezes maior.


Testes estáticos


Após a montagem, é realizado o teste estático: pegamos a pasta com a compilação e executamos um conjunto de verificações de todo o conteúdo existente. O código também é conteúdo, portanto sua análise estática (cppcheck + PVS-Studio) também está aqui.

Em um habr, havia um material detalhado sobre como implementamos o teste estático, eu recomendo. Enfatizo apenas que os testes estáticos após a compilação e nos ganchos de pré-confirmação são executados pelo mesmo código. Isso simplifica bastante o suporte ao sistema.

Testes de tempo de execução


Se a compilação de testes estáticos foi bem-sucedida, você pode prosseguir e tentar executar a compilação montada. Estamos testando compilações em todas as plataformas, exceto UWP, ou seja, Windows, MacOs, iOS, Android. UWP - também será, mas um pouco mais tarde.

Por que testar a criação de desktops, se eles parecem necessários apenas no desenvolvimento? A resposta para a pergunta é: é ruim se um artista ou um designer de nível obtiver uma compilação que trava na inicialização por algum motivo ridículo. Portanto, o Smoke-Test, o conjunto mínimo de verificações de jogabilidade e jogabilidade básica, é realizado para todas as plataformas.

Tudo o que foi escrito acima sobre compilações também é válido para testes em dispositivos - pelo menos uma vez por dia. Com poucas exceções: existem testes muito longos que não têm tempo para concluir em um dia.

Testes de fumaça são executados para cada confirmação. A conclusão bem-sucedida das verificações básicas é um pré-requisito para a construção entrar no sistema de distribuição. Geralmente, não faz sentido conceder a alguém acesso a uma construção que obviamente não funciona. Aqui você pode objetar e criar exceções. Os projetos têm uma solução alternativa para dar acesso a uma compilação que não funciona, mas eles dificilmente a usam.

Que outros testes existem:

  • Referência: verificamos o desempenho no FPS e na memória em várias situações e em todos os dispositivos.
  • Testes unitários de combinação 3: cada elemento e cada mecânica são testados individualmente e em todas as combinações de interação.
  • A passagem de todo o jogo do início ao fim.
  • Vários testes de regressão, por exemplo, testes de localização ou que todas as janelas da interface do usuário sejam abertas corretamente ou que as cenas de fishdom em Fishdom sejam reproduzidas sem erros.
  • Mesmo assim, mas com AddressSanitizer .
  • Testes de compatibilidade para versões de jogos: leve o usuário para salvar o arquivo das versões anteriores, abra-o na nova versão e verifique se está tudo bem.
  • Vários testes personalizados relevantes para o mecânico de um projeto específico.

Para executar os testes, usamos nosso próprio suporte de teste para dispositivos iOS e Android. Isso nos permite iniciar com flexibilidade as compilações necessárias nos dispositivos, interagir com o dispositivo a partir do código. Temos controle total, um nível compreensível de confiabilidade, sabemos quais problemas podemos enfrentar e quanto tempo levará para resolvê-los. Nenhum dos serviços em nuvem que fornecem dispositivos de teste oferece esse conforto.

GATOS


Os testes listados acima são implementados dentro do código do projeto. Isso permite, em teoria, fazer um teste de qualquer complexidade, mas requer esforço e recursos do desenvolvimento do projeto para implementar e dar suporte a esses testes. Esses recursos geralmente não estão disponíveis e o teste manual de regressão múltipla é difícil e desnecessário. Eu realmente queria que os testadores fizessem a automação de testes. E criamos uma estrutura para eles - o Sistema de Teste de Automação Contínua, ou CATS.

Qual é a ideia: permitir que os autores do script de teste interajam com o aplicativo de jogos, absolutamente sem se importar com o modo como tudo funciona. Escrevemos scripts em python primitivo, acessamos o aplicativo através de um conjunto de abstrações. Por exemplo: "Homescapes, abra-me uma vitrine e compre esse e aquele produto". Veja o resultado, bingo.

Toda a implementação dos comandos de script está oculta por trás de um conjunto de abstrações. A API que implementa a interação com o aplicativo permite executar qualquer ação de várias maneiras:

  • envie uma solicitação http para o servidor, que está embutido no mecanismo de jogo, com algum tipo de comando. Este comando é processado pelo código do jogo. Geralmente isso é algum tipo de trapaça, pode ser arbitrariamente simples ou complexo. Por exemplo, "forneça as coordenadas do centro do botão com o identificador especificado". Ou "passe-me o jogo daqui para o nível com o número indicado".
  • Podemos abrir uma janela através do truque ou descobrir as coordenadas do botão pelo qual essa janela se abre, podemos emular o clique nesse botão, podemos executar um clique virtual nele.
  • Finalmente, podemos executar um clique "real" nas coordenadas especificadas, como se isso fosse feito com um dedo na tela.

O último método abre espaço para a imaginação dos testadores que frequentemente desejam testar compilações de "combate", onde não há truques. Manter esses cenários é mais difícil, mas uma "compilação de combate" é uma compilação de "combate".


Acabou sendo muito conveniente trabalhar com as coordenadas do centro do botão: as coordenadas às vezes mudam, mas os identificadores de botão são raros. Isso levou a outra propriedade importante do sistema: a capacidade de escrever um script de teste para todas as plataformas e todas as resoluções de tela.

Entrega, relatórios e rastreamentos


Com a entrega, tudo ficou bem simples: usamos um único armazenamento compartilhado para criar artefatos e para armazenamento no sistema de distribuição. O "carregamento" da compilação se resume a chamar um par de solicitações para a API do serviço de distribuição da compilação, basicamente registrando-se. Dessa forma, economizamos um pouco de tempo em bombear construções e dinheiro para seu armazenamento.

Lembre-se de que você falou em minimizar os recursos necessários para corrigir erros encontrados nas compilações? Relatórios e trilhas - exatamente sobre isso:

  • Relatar um problema encontrado é uma tarefa no Asana. É fácil de controlar, atribua-o ao desenvolvedor certo, transfira-o para a equipe de IC se algo der errado na infraestrutura.
  • Coletamos compilações para cada confirmação. Conhecemos o autor desse commit, portanto somente ele verá essa tarefa. Portanto, economizamos tempo para outros desenvolvedores: eles não precisam se distrair com problemas com os quais não têm nada a ver e ajudar a resolver os quais, provavelmente, eles não conseguirão.
  • Se você criar uma compilação a partir do próximo commit, provavelmente ainda está com defeito. Haverá um comentário na tarefa: “A compilação ainda está quebrada”, o autor do novo commit não verá a tarefa e não perderá tempo com o problema de outra pessoa.
  • Enviamos relatórios para o Slack. Necessariamente - pessoalmente para quem "quebrou" a compilação e, se o projeto quiser - para um canal especial ou para qualquer funcionário da Playrix. Tudo é o mais flexível possível.

Rastreios são necessários para que em todos os lugares haja informações completas sobre a construção e as alterações das quais ela foi coletada. Para não procurar nada, para que tudo estivesse à mão e não houvesse necessidade de gastar tempo procurando detalhes que geralmente são necessários ao pesquisar um problema.

  • O relatório contém um link para a compilação, o log de compilação, o texto do erro de compilação encontrado e os nomes dos testes invertidos. Geralmente, um programador, após receber uma tarefa de relatório, pode corrigir imediatamente o erro: o nome do arquivo, a linha e o texto do erro estão no relatório.
  • A mensagem no Slack contém todos os mesmos + um link para a tarefa no Asana.
  • No Teamcity, um link para uma tarefa. O engenheiro de construção pode entrar imediatamente na tarefa; em um clique, você não precisa procurar nada.
  • No github - status com um link para a compilação, no comentário ao commit - um link para a tarefa para a qual essa compilação foi feita. Na tarefa - um comentário com um link para o commit.
  • No serviço de distribuição de build: link para o build, link para o commit.

Não há nada a lembrar, mas você entendeu a ideia: links para tudo, em qualquer lugar. Isso acelera bastante o estudo de qualquer situação incompreensível.

Fazenda


Coletamos e testamos compilações para todas as plataformas. Para isso, precisamos de muitos agentes diferentes. Rastrear e mantê-los manualmente é longo e difícil. Todos os agentes são preparados automaticamente. Usamos Packer e Ansible.

Todos os logs de todos os agentes, Teamcity, todos os serviços existentes, salvamos (no nosso caso - no ELK). Todos os serviços, processando uma compilação, adicionam o número dessa compilação a cada linha de logs. Podemos ver em uma única solicitação todo o ciclo de vida da construção, desde a sua aparência na fila até o final do envio de todos os relatórios.

Implementamos nosso próprio mecanismo de otimização de fila. O do Teamcity não funciona muito bem em nossos números. Falando em números:

  • Coletamos cerca de 5.000 compilações todos os dias. São cerca de 500 horas de trabalho da máquina.
  • A construção do milionésimo milionésimo foi há um mês.
  • Temos mais de 50 servidores de compilação em 10 locais diferentes.
  • Mais de 40 dispositivos móveis em um banco de testes.
  • Exatamente 1 servidor Teamcity.


IC como serviço


O Playrix CI é um serviço. Existem muitos projetos, muitas idéias também.

Otimizamos o tempo desde a inserção da construção na fila até o final de sua execução, porque é exatamente isso que o "tempo de construção" é considerado pelo usuário do serviço, o desenvolvedor. Isso nos permite pesquisar e encontrar um equilíbrio entre o tempo de construção e o tempo gasto na fila. Parece lógico que, com o crescimento da empresa e o número de projetos, o farm de criação que coleta esses projetos também crescerá. Mas, graças às otimizações, a taxa de crescimento da fazenda está muito aquém da taxa de crescimento da empresa.

Qualquer otimização começa com o monitoramento, com uma coleção metódica de estatísticas. Coletamos muitas estatísticas e sabemos absolutamente tudo sobre nossas compilações. Mas, além do volume do farm de builds, também há uma equipe que oferece suporte ao sistema de IC e faz com que ele não precise pensar sobre de onde vêm os builds.

A otimização de processos nessa equipe também é um processo interessante e divertido.Por exemplo, escrevemos testes para definir configurações de compilações, porque existem muitas dessas configurações, sem testes semelhantes, não é fácil encontrar todos os locais que precisam ser editados. Para quase todas as alterações, primeiro escrevemos um teste e depois fazemos, ou seja, temos TDD. Existem muitos processos relacionados a tarefas, gerenciamento de incidentes, agendamento do fluxo de tarefas recebidas.

Os desenvolvedores devem se concentrar na criação de jogos excelentes e bem-sucedidos sem se preocupar com a origem das compilações. Para isso, o Playrix tem um IC. Deve haver uma meta que você deseja alcançar com a Integração Contínua, um problema que precisa ser resolvido. É importante não apresentar um problema, ou seja, encontrá-lo. E quando a encontrar, lembre-se da nossa experiência e faça melhor. E lembre-se: a

CI nunca dorme
Vejo você!

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


All Articles