Compartilhar via


DevOps

Dica

Esse conteúdo é um trecho do livro eletrônico, para Projetar os Aplicativos .NET nativos de nuvem para o Azure, disponível no .NET Docs ou como um PDF para download gratuito que pode ser lido offline.

Cloud Native .NET apps for Azure eBook cover thumbnail.

O mantra favorito dos consultores de software é responder "Depende" para qualquer pergunta. Não é porque os consultores de software gostam de evitar tomar uma posição. Mas sim porque não há uma única resposta verdadeira para perguntas sobre software. Não há respostas totalmente certas e erradas, mas sim um equilíbrio entre opostos.

Por exemplo, considere as duas principais escolas de desenvolvimento de aplicativos Web: SPAs (aplicativos de página única) versus aplicativos no lado do servidor. Por um lado, a experiência do usuário tende a ser melhor com os SPAs, e a quantidade de tráfego para o servidor Web pode ser minimizada, possibilitando hospedá-los em algo tão simples quanto a hospedagem estática. Por outro lado, o desenvolvimento dos SPAs tende a demorar mais e seus testes são mais difíceis. Qual é a escolha certa para mim? Bem, depende da sua situação.

Aplicativos nativos de nuvem não são imunes à mesma dicotomia. Eles têm vantagens claras em termos de velocidade de desenvolvimento, estabilidade e escalabilidade, mas seu gerenciamento pode ser um pouco mais difícil.

Anos atrás, não era incomum que o processo de migração de um aplicativo do desenvolvimento para a produção demorasse um mês ou mais. As empresas lançavam softwares a cada 6 meses ou até mesmo anualmente. Não é necessário procurar além do Microsoft Windows para ter uma ideia do ritmo de lançamento das versões que era aceitável antes do Windows 10. Cinco anos se passaram entre o Windows XP e o Vista, outros três entre o Vista e o Windows 7.

Agora já está bem estabelecido que conseguir lançar rapidamente um software proporciona às empresas mais rápidas uma enorme vantagem de mercado sobre os concorrentes mais lentos. É por esse motivo que as principais atualizações para Windows 10 agora ocorrem aproximadamente a cada seis meses.

Os padrões e práticas que permitem versões mais rápidas e confiáveis para fornecer valor ao negócio são coletivamente conhecidos como DevOps. Consistem em uma ampla gama de ideias que abrangem todo o ciclo de vida de desenvolvimento de software, desde a especificação de um aplicativo até a entrega e a operação desse aplicativo.

DevOps surgiu antes dos microsserviços e é provável que o movimento em direção a serviços menores e mais adequados para os propósitos não teria sido possível sem o DevOps para facilitar a liberação e a operação não apenas de um, mas de muitos aplicativos em produção.

Figure 10-1 Search trends show that the growth in microservices doesn't start until after DevOps is a fairly well-established idea.

Figure 10-1 – DevOps e microsserviços.

Por meio de boas práticas de DevOps, é possível obter as vantagens de aplicativos nativos de nuvem sem sufocar quem realmente opera os aplicativos sob uma montanha de trabalho.

Não dá para usar sempre a mesma ferramenta quando se trata de DevOps. Ninguém pode vender uma solução completa e abrangente para liberar e operar aplicativos de alta qualidade. Isso ocorre porque cada aplicativo é totalmente diferente de todos os outros. No entanto, há ferramentas que podem deixar a proposta do DevOps muito menos assustadora. Uma dessas ferramentas é o Azure DevOps.

Azure DevOps

Azure DevOps tem uma longa linhagem. É possível rastrear suas raízes até quando o Team Foundation Server ficou online pela primeira vez e por meio das várias alterações de nome: Visual Studio Online e Visual Studio Team Services. Mas, ao longo dos anos, tornou-se muito mais do que seus antecessores.

O Azure DevOps é dividido em cinco componentes principais:

Figure 10-2 The five major areas of Azure DevOps

Figura 10-2 – Azure DevOps.

Azure Repos – Gerenciamento de código-fonte que dá suporte ao venerável TFVC (Controle de Versão do Team Foundation) e ao Git favorito do setor. As solicitações de pull proporcionam uma maneira de habilitar a codificação social promovendo a discussão sobre as mudanças conforme elas ocorrem.

Azure Boards – Fornece uma ferramenta de acompanhamento de item de trabalho e problemas que se esforça para permitir que os usuários escolham os fluxos de trabalho que funcionam melhor para eles. Ele vem com vários modelos pré-configurados, incluindo os que dão suporte aos estilos de desenvolvimento SCRUM e Kanban.

Azure Pipelines – Um sistema de gerenciamento de builds e lançamento que dá suporte a uma integração forte com o Azure. É possível executar os builds em várias plataformas, do Windows ao Linux e ao macOS. Agentes de build podem ser provisionados na nuvem ou no local.

Azure Test Plans – Nenhum funcionário de garantia de qualidade ficará para trás com o gerenciamento de teste e o suporte a testes exploratórios oferecidos pelo recurso Test Plans.

Azure Artifacts – Um feed de artefatos que permite que as empresas criem suas próprias versões internas do NuGet, npm, etc. Ele tem dupla finalidade ao atuar como um cache de pacotes upstream se houver falha em um repositório centralizado.

A unidade organizacional de nível superior no Azure DevOps é chamada de Projeto. Em cada projeto, é possível ativar e desativar os diversos componentes, como o Azure Artifacts. Cada um desses componentes oferece vantagens diferentes para aplicativos nativos de nuvem. Os três mais úteis são repositórios, quadros e pipelines. Se os usuários quiserem gerenciar seu código-fonte em outra pilha de repositórios, como o GitHub, mas ainda aproveitando o Azure Pipelines e outros componentes, isso é perfeitamente possível.

Felizmente, as equipes de desenvolvimento têm muitas opções ao selecionar um repositório. Um deles é o GitHub.

GitHub Actions

Fundado em 2009, o GitHub é um repositório baseado na Web extremamente popular para hospedar projetos, documentação e código. Muitas grandes empresas de tecnologia, como a Apple, Amazon, Google e grandes corporações, usam o GitHub. O GitHub usa como sua base o sistema de controle de versão distribuída do software livre Git. Ele também adiciona seu próprio conjunto de recursos, incluindo acompanhamento de defeitos, solicitações de pull e recursos, gerenciamento de tarefas e wikis para cada base de código.

À medida que o GitHub evolui, também adiciona recursos do DevOps. Por exemplo, o GitHub tem seu próprio pipeline de CI/CD (integração contínua/entrega contínua), o GitHub Actions. GitHub Actions é uma ferramenta de automação de fluxo de trabalho alimentada pela comunidade. Ele permite que as equipes do DevOps se integrem às ferramentas existentes, combinem e correspondam novos produtos e se conectem ao ciclo de vida dos softwares, incluindo os parceiros existentes de CI/CD."

O GitHub tem mais de 40 milhões de usuários, sendo o maior host de código-fonte do mundo. Em outubro de 2018, a Microsoft comprou o GitHub. A Microsoft prometeu que o GitHub continuará a ser uma plataforma aberta na qual qualquer desenvolvedor poderá se conectar e estender. Continuará funcionando como uma empresa independente. O GitHub oferece planos para contas corporativas, de equipe, profissionais e gratuitas.

Controle do código-fonte

Organizar o código para um aplicativo nativo de nuvem pode ser um grande desafio. Em vez de um único aplicativo gigante, os aplicativos nativos de nuvem tendem a ser compostos por aplicativos Web menores que conversam entre si. Como acontece em tudo na computação, a melhor disposição de código continua sendo uma questão em aberto. Há exemplos de aplicativos bem-sucedidos usando diferentes tipos de layouts, mas duas variantes parecem ter a maior popularidade.

Antes de saber mais sobre o controle do código-fonte real, provavelmente vale a pena decidir quantos são os projetos apropriados. Em um único projeto, há suporte para vários repositórios e pipelines de build. Os quadros são um pouco mais complicados, mas também facilitam atribuir as tarefas a várias equipes em um único projeto. É possível dar suporte a centenas ou mesmo milhares de desenvolvedores em um único projeto do Azure DevOps. Provavelmente essa é a melhor abordagem, pois fornece um único lugar para todos os desenvolvedores trabalharem e reduz a confusão de encontrar um aplicativo quando os desenvolvedores não têm certeza do projeto em que ele reside.

Dividir o código para microsserviços no projeto do Azure DevOps pode ser um pouco mais desafiador.

Figure 10-3 Single versus Multiple Repositories

Figura 10-3 – Um único repositório versus vários repositórios.

Repositório por microsserviço

À primeira vista, essa parece ser a abordagem mais lógica para dividir o código-fonte para microsserviços. Cada repositório pode conter o código necessário para criar um microsserviço. É fácil perceber as vantagens dessa abordagem:

  1. Instruções para criar e manter o aplicativo podem ser adicionadas a um arquivo LEIAME na raiz de cada repositório. Ao inverter entre os repositórios, é fácil encontrar essas instruções, reduzindo o tempo de rotação para os desenvolvedores.
  2. Cada serviço está localizado em um local lógico e fácil de encontrar quando se sabe o nome do serviço.
  3. É possível configurar facilmente os builds de modo que só sejam disparados quando uma alteração for feita no repositório proprietário.
  4. O número de alterações que chegam a um repositório é limitado ao pequeno número de desenvolvedores que trabalham no projeto.
  5. É fácil configurar a segurança restringindo os repositórios aos quais os desenvolvedores têm permissões de leitura e gravação.
  6. As configurações de nível de repositório podem ser alteradas pela equipe proprietária com um mínimo de discussão com outras pessoas.

Uma das principais ideias por trás dos microsserviços é que os serviços devem ser mantidos em silos e separados uns dos outros. Ao usar o Design Orientado pelo Domínio para decidir sobre os limites dos serviços, esses serviços atuam como limites transacionais. As atualizações de bancos de dados não devem abranger vários serviços. Essa coleção de dados relacionados é conhecida como contexto limitado. Essa ideia é refletida pelo isolamento de dados de microsserviço para um banco de dados separado e autônomo dos outros serviços. Faz muito sentido levar essa ideia até o código-fonte.

No entanto, essa abordagem ainda tem seus problemas. Um dos problemas de desenvolvimento mais complicados do nosso tempo é o gerenciamento de dependências. Considere o número de arquivos que compõem o diretório node_modules médio. Uma nova instalação de algo como create-react-app provavelmente terá milhares de pacotes. A questão de como gerenciar essas dependências é difícil de solucionar.

Se uma dependência for atualizada, os pacotes de downstream também deverão atualizar essa dependência. Infelizmente, isso requer um trabalho de desenvolvimento que faz com que, invariavelmente, o node_modules diretório acabe com várias versões de um único pacote, sendo cada uma a dependência de algum outro pacote que é uma versão em um ritmo ligeiramente diferente. Ao implantar um aplicativo, qual versão de uma dependência deve ser usada? A versão que está atualmente em produção? A versão que está atualmente em Beta, mas provavelmente estará em produção quando o consumidor chegar à produção? Esses são problemas difíceis que não são resolvidos usando apenas microsserviços.

Há bibliotecas que dependem de uma grande variedade de projetos. Dividir os microsserviços colocando cada um em um repositório, é possível solucionar melhor as dependências internas usando o repositório interno, Azure Artifacts. Builds para bibliotecas enviarão por push suas versões mais recentes para o Azure Artifacts para consumo interno. O projeto em downstream ainda deve ser atualizado manualmente para assumir uma dependência dos pacotes recém-atualizados.

Outra desvantagem se apresenta ao mover o código entre os serviços. Embora seja bom acreditar que a primeira divisão de um aplicativo em microsserviços está 100% correta, a realidade é que raramente conseguimos prever tudo a ponto de não cometer erros de divisão de serviço. Assim, a funcionalidade e o código que a direciona precisarão mudar de acordo com o serviço e com o repositório. Ao mover de um repositório para outro, o código perde seu histórico. Há muitos casos, especialmente em auditorias, em que ter o histórico completo em um código é excelente.

A última e mais importante desvantagem é a coordenação de alterações. Em um verdadeiro aplicativo de microsserviços, não deve haver dependências de implantação entre serviços. Deve ser possível implantar os serviços A, B e C em qualquer ordem, já que eles têm acoplamento solto. No entanto, na verdade há momentos em que é desejável fazer uma alteração que alcance vários repositórios ao mesmo tempo. Alguns exemplos incluem a atualização de uma biblioteca para fechar uma falha de segurança ou a alteração de um protocolo de comunicação usado por todos os serviços.

Para fazer uma alteração entre repositórios, é necessário obter uma confirmação sucessiva de cada repositório. Será necessário solicitar por pull as alterações em cada repositório e depois revisá-las separadamente. Essa atividade pode ser difícil de coordenar.

Uma alternativa ao uso de vários repositórios é colocar todo o código-fonte em um único repositório gigante.

Um único repositório

Nessa abordagem, às vezes chamada de monorrepositório, os códigos-fonte completos de todos os serviços são colocado no mesmo repositório. À primeira vista, essa abordagem parece ser uma ideia terrível que provavelmente desorganizará o manuseio do código-fonte. Mas há algumas vantagens marcantes para trabalhar dessa forma.

A primeira vantagem é que é mais fácil gerenciar dependências entre projetos. Em vez de depender de algum feed de artefato externo, é possível importar os projetos diretamente de uns para os outros. Ou seja, as atualizações são instantâneas e provavelmente as versões conflitantes serão encontradas no momento da compilação na estação de trabalho do desenvolvedor. Na verdade, deslocando alguns dos testes de integração restantes.

Mover o código entre projetos facilita preservar o histórico, pois os arquivos serão detectados como tendo sido movidos em vez de reescritos.

Outra vantagem é que alterações abrangentes que ultrapassam os limites de serviço podem ser feitas com uma única confirmação. Essa atividade reduz a sobrecarga de ter dezenas de alterações em potencial a serem analisadas individualmente.

Há muitas ferramentas que podem executar a análise estática do código para detectar práticas de programação inseguras ou uso problemático de APIs. Em um mundo de vários repositórios, será necessário executar a iteração de cada repositório para encontrar os problemas. Um único repositório permite executar a análise em um só lugar.

A abordagem de repositório único também apresenta muitas desvantagens. Uma das mais preocupantes é que ter um único repositório gera preocupações com a segurança. Se o conteúdo de um repositório for vazado em um repositório por modelo de serviço, a quantidade de código perdido será mínima. Já com um único repositório, uma empresa poderá perder tudo aquilo que possui. Houve muitos exemplos no passado disso acontecendo, frustrando todos os esforços de desenvolvimento de jogos. Ter vários repositórios expõe menos área de superfície, o que é uma característica desejável para a maioria das práticas de segurança.

É provável que o tamanho do repositório único rapidamente se torne incontrolável. Isso apresenta algumas implicações de desempenho interessantes. Pode ser necessário usar ferramentas especializadas, como o Sistema de Arquivos Virtuais para Git, que foi originalmente projetado para melhorar a experiência dos desenvolvedores na equipe do Windows.

Geralmente, o argumento para usar um único repositório se resume ao fato de que o Facebook ou o Google usam esse método para definir a disposição do código-fonte. Se a abordagem é boa o suficiente para essas empresas, então, certamente, é adequada para todas as empresas. A verdade é que poucas empresas operam em algo na escala do Facebook ou do Google. Os problemas que ocorrem nessas escalas são diferentes daqueles que a maioria dos desenvolvedores enfrentará. O que é bom para um pode não ser bom para o outro.

No final, é possível usar qualquer solução para hospedar o código-fonte para microsserviços. No entanto, na maioria dos casos, a sobrecarga no gerenciamento e na engenharia para operar em um único repositório não vale as poucas vantagens. Dividir o código em vários repositórios incentiva uma melhor separação de preocupações e a autonomia entre as equipes de desenvolvimento.

Estrutura do diretório padrão

Independentemente do debate de um único repositório versus vários repositórios, cada serviço terá seu próprio diretório. Uma das melhores otimizações para permitir que os desenvolvedores passem rapidamente entre projetos é manter uma estrutura de diretório padrão.

Figure 10-4 A standard directory structure for both the email and sign-in services

Figura 10-4 – Estrutura do diretório padrão.

Sempre que um novo projeto for criado, será necessário usar um modelo que coloque em prática a estrutura correta. Esse modelo também pode incluir itens úteis, como a estrutura de um arquivo README e azure-pipelines.yml. Em qualquer arquitetura de microsserviço, um alto grau de variação entre projetos dificulta as operações em massa em relação aos serviços.

Há diversas ferramentas que podem fornecer modelagem para um diretório inteiro, contendo vários diretórios de código-fonte. Yeoman é popular no mundo JavaScript e o GitHub lançou recentemente modelos de repositório, que fornecem grande parte da mesma funcionalidade.

Gerenciamento de tarefas

O gerenciamento de tarefas de qualquer projeto pode ser difícil. Primeiro, há inúmeras perguntas a serem respondidas sobre o tipo de fluxos de trabalho a configurar para garantir a produtividade ideal do desenvolvedor.

Os aplicativos nativos de nuvem tendem a ser menores do que os produtos de software tradicionais ou pelo menos são divididos em serviços menores. O acompanhamento de problemas ou tarefas relacionadas a esses serviços permanece tão importante quanto qualquer outro projeto de software. Ninguém quer perder o controle de algum item de trabalho ou explicar a um cliente que seu problema não foi registrado corretamente. Os quadros são configurados no nível do projeto, mas as áreas podem ser definidas para cada projeto. Elas permitem dividir os problemas em vários componentes. A vantagem de manter todo o trabalho do aplicativo inteiro em um só lugar é que fica fácil mover itens de trabalho de uma equipe para outra porque fica mais fácil de compreendê-los.

O Azure DevOps vem com vários modelos populares pré-configurados. Na configuração mais básica, tudo o que é necessário saber é o que está na lista de pendências, no que as pessoas estão trabalhando e o que é feito. É importante ter essa visibilidade do processo de criação de software para que seja possível priorizar o trabalho e relatar as tarefas concluídas ao cliente. É claro que poucos projetos de software se apegam a um processo tão simples quanto to do, doing e done. Não demora muito para que as pessoas comecem a adicionar etapas como QA ou Detailed Specification ao processo.

Uma das partes mais importantes das metodologias Agile é a auto-introspecção em intervalos regulares. Essas revisões destinam-se a fornecer informações sobre quais problemas a equipe está enfrentando e como elas podem ser melhoradas. Com frequência, isso significa alterar o fluxo de problemas e recursos por meio do processo de desenvolvimento. Assim, a integridade não é afetada com a expansão dos layouts das placas com estágios adicionais.

Os estágios nos quadros não são a única ferramenta organizacional. Dependendo da configuração do quadro, há uma hierarquia de itens de trabalho. O item mais granular que pode aparecer em um quadro é uma tarefa. Fora da caixa, uma tarefa contém campos para título, descrição, prioridade, uma estimativa da quantidade de trabalho restante e a capacidade de se vincular a outros itens de trabalho ou de desenvolvimento (branches, confirmações, solicitações de pull, builds e assim por diante). Os itens de trabalho podem ser classificados em diferentes áreas do aplicativo e diferentes iterações (sprints) para facilitar sua localização.

Figure 10-5 An example task in Azure DevOps

Figura 10-5 – Tarefa no Azure DevOps.

O campo de descrição dá suporte aos estilos normais que se espera (negrito, sublinhado, itálico e tachado) e à capacidade de inserir imagens. Isso o torna uma ferramenta poderosa para usar ao especificar o trabalho ou bugs.

As tarefas podem ser distribuídas em recursos, que definem uma unidade de trabalho maior. Por sua vez, os recursos podem ser acumulados em épicos. Classificar tarefas nessa hierarquia facilita bastante para entender o quanto falta para implantar um recurso grande.

Figure 10-6 Work item types configured by default in the Basic process template

Figura 10-6 – Item de trabalho no Azure DevOps.

No Azure Boards, há diferentes tipos de modos de exibição dos problemas. Os itens que ainda não estão programados aparecem na lista de pendências. A partir daí, eles podem ser atribuídos a um sprint. Sprint é uma caixa de tempo durante o qual se espera que alguma quantidade de trabalho seja concluída. Esse trabalho pode incluir tarefas, mas também a resolução de tíquetes. Uma vez lá, todo o sprint inteiro pode ser gerenciado na seção do quadro Sprint. Este modo de exibição mostra como o trabalho está progredindo e inclui um gráfico de burndown para fornecer uma estimativa de sucesso para o sprint.

Figure 10-7 A board with a sprint defined

Figura 10-7 – Quadro no Azure DevOps.

Agora deve estar evidente que os Quadros do Azure DevOps disponibilizam um poder muito grande. Para os desenvolvedores, é fácil exibir o que está sendo trabalhado. Os gerentes de projeto conseguem visualizar o próximo trabalho e têm uma visão geral do trabalho existente. Para os gerentes, há muitos relatórios sobre recursos e capacidade. Infelizmente, não há nada mágico em aplicativos nativos de nuvem que elimine a necessidade de acompanhar o trabalho. Mas se você precisar acompanhar o trabalho, há alguns locais em que a experiência é melhor do que no Azure DevOps.

Pipelines de CI/CD

Quase nenhuma alteração no ciclo de vida do desenvolvimento de software foi tão revolucionária quanto o advento da CI (integração contínua) e da CD (entrega contínua). Compilar e executar testes automatizados em relação ao código-fonte de um projeto assim que uma alteração for verificada captura antecipadamente os erros. Antes do surgimento de builds de integração contínua, não era incomum extrair o código do repositório e descobrir que ele não passou em testes ou que nem poderia ser criado. Isso resultou no rastreamento da origem da interrupção.

Tradicionalmente, o envio de software para o ambiente de produção exigia uma documentação abrangente e uma lista de etapas. Cada uma dessas etapas precisava ser concluída manualmente em um processo muito propenso a erros.

Figure 10-8 A checklist

Figura 10-8 – Lista de verificação.

A irmã da integração contínua é a entrega contínua, na qual os pacotes recém-criados são implantados em um ambiente. O processo manual não pode ser dimensionado para corresponder à velocidade do desenvolvimento, portanto, a automação se torna mais importante. As listas de verificação foram substituídas por scripts que podem executar as mesmas tarefas com mais rapidez e precisão do que qualquer humano.

O ambiente para o qual a entrega contínua fornece a entrega pode ser um ambiente de teste ou, como é feito por muitas grandes empresas de tecnologia, o ambiente de produção. Este último requer um investimento em testes de alta qualidade que podem garantir que uma mudança não vai interromper a produção para os usuários. Da mesma forma que a integração contínua detectou problemas no código, a entrega contínua precoce captura antecipadamente os problemas no processo de implantação.

A importância de automatizar o processo de build e entrega é acentuada por aplicativos nativos de nuvem. As implantações ocorrem com mais frequência e para mais ambientes, portanto, a implantação manual é praticamente impossível.

Azure Builds

O Azure DevOps fornece um conjunto de ferramentas para tornar a integração e a implantação contínuas mais fácil do que nunca. Essas ferramentas ficam no Azure Pipelines. A primeiro é o Azure Builds, uma ferramenta para executar definições de build baseadas em YAML em escala. Os usuários podem trazer seus próprios computadores de build (ótimo para o caso do build exigir um ambiente de configuração meticulosa) ou usar um computador de um pool constantemente atualizado de máquinas virtuais hospedadas no Azure. Esses agentes de build hospedados vêm pré-instalados com uma ampla gama de ferramentas de desenvolvimento não apenas para o desenvolvimento do .NET, mas para tudo, desde Java a Python e até para o desenvolvimento do iPhone.

O DevOps inclui uma grande variedade de definições de build que podem ser personalizadas para qualquer build. As configurações de build são definidas em no arquivo azure-pipelines.yml e verificadas no repositório para que possam ser colocadas em uma versão junto com o código-fonte. Dessa forma, fica mais fácil fazer alterações no pipeline de build em um branch, pois é possível verificar as alterações apenas nessa ramificação. A Figura 10-9 mostra um exemplo de azure-pipelines.yml para a criação de um aplicativo Web ASP.NET na estrutura completa.

name: $(rev:r)

variables:
  version: 9.2.0.$(Build.BuildNumber)
  solution: Portals.sln
  artifactName: drop
  buildPlatform: any cpu
  buildConfiguration: release

pool:
  name: Hosted VisualStudio
  demands:
  - msbuild
  - visualstudio
  - vstest

steps:
- task: NuGetToolInstaller@0
  displayName: 'Use NuGet 4.4.1'
  inputs:
    versionSpec: 4.4.1

- task: NuGetCommand@2
  displayName: 'NuGet restore'
  inputs:
    restoreSolution: '$(solution)'

- task: VSBuild@1
  displayName: 'Build solution'
  inputs:
    solution: '$(solution)'
    msbuildArgs: '-p:DeployOnBuild=true -p:WebPublishMethod=Package -p:PackageAsSingleFile=true -p:SkipInvalidConfigurations=true -p:PackageLocation="$(build.artifactstagingdirectory)\\"'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: VSTest@2
  displayName: 'Test Assemblies'
  inputs:
    testAssemblyVer2: |
     **\$(buildConfiguration)\**\*test*.dll
     !**\obj\**
     !**\*testadapter.dll
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: CopyFiles@2
  displayName: 'Copy UI Test Files to: $(build.artifactstagingdirectory)'
  inputs:
    SourceFolder: UITests
    TargetFolder: '$(build.artifactstagingdirectory)/uitests'

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'
    ArtifactName: '$(artifactName)'
  condition: succeededOrFailed()

Figura 10-9 – Amostra de azure-pipelines.yml

Essa configuração de build usa uma série de tarefas internas que tornam a criação de builds tão simples quanto montar um conjunto de Lego (mais simples que a enorme Millennium Falcon). Por exemplo, a tarefa NuGet restaura pacotes NuGet, enquanto a tarefa VSBuild chama as ferramentas de build do Visual Studio para executar a atual compilação. Há centenas de tarefas diferentes disponíveis no Azure DevOps, com outras milhares que são mantidas pela comunidade. É provável que, independentemente das tarefas de build que você está procurando executar, alguém já tenha criado algo similar.

Os builds podem ser disparados manualmente, por um check-in, por agendamento ou pela conclusão de outro build. Na maioria dos casos, é desejável o build a cada check-in. Os builds podem ser filtrados para que diferentes builds sejam executados em diferentes branches ou partes do repositório. Isso possibilita criar cenários para executar builds rápidos com testes reduzidos em solicitações de pull e para executar um pacote de regressão completo no tronco todas as noites.

O resultado final de um build é uma coleção de arquivos conhecidos como artefatos de build. Esses artefatos podem ser passados para a próxima etapa no processo de build ou adicionados a um feed do Azure Artifacts para que possam ser consumidos por outras compilações.

Versões do Azure DevOps

Builds cuidam da compilação do software em um pacote que pode ser enviado, mas os artefatos ainda precisam ser enviados por push para um ambiente de teste a fim de concluir a entrega contínua. Para isso, o Azure DevOps usa uma ferramenta separada chamada Versões. A ferramenta Versões usa a biblioteca das mesmas tarefas que estavam disponíveis para o Build, mas introduz o conceito de "estágios". Um estágio é um ambiente isolado no qual o pacote é instalado. Por exemplo, um produto pode usar um ambiente de desenvolvimento, de garantia de qualidade e de produção. O código é entregue continuamente no ambiente de desenvolvimento no qual é possível executar os testes automatizados. Depois de ser aprovada nesses testes, a versão passará para o ambiente de garantia de qualidade para a execução de testes manuais. Por fim, o código é enviado por push para a produção, ficando visível para todos.

Figure 10-10 An example release pipeline with Develop, QA, and Production phases

Figura 10-10 – Pipeline de lançamento

Cada estágio do build pode ser disparado automaticamente pela conclusão da fase anterior. No entanto, isso não é desejável em diversos casos. Mover o código para a produção pode exigir a aprovação de alguém. Há suporte para isso na ferramenta Versões, que permite aprovadores em cada etapa do pipeline de lançamento. As regras podem ser configuradas de modo que uma pessoa ou grupo específico desative uma versão antes que entre em produção. Essas barreiras permitem verificações manuais de qualidade e também de conformidade com quaisquer requisitos regulatórios relacionados a controlar o que entra em produção.

Todo mundo obtém um pipeline de build

Não há custo para configurar vários pipelines de build, portanto, é vantajoso ter pelo menos um pipeline de build por microsserviço. O ideal é que os microsserviços possam ser implantados independentemente do ambiente, portanto, permitir que cada um seja lançado por meio de seu próprio pipeline sem liberar uma massa de código não relacionado é perfeito. Cada pipeline pode ter seu próprio conjunto de aprovações, permitindo variações no processo de build para cada serviço.

Lançamentos de controle de versão

Uma desvantagem de usar a funcionalidade de Versões é que ela não pode ser configurada como um arquivo azure-pipelines.yml com check-in. Há muitos motivos pelos quais você pode querer fazer isso, que vão de ter definições de versão por branch até incluir um esqueleto de versão em seu modelo de projeto. Por sorte, o trabalho está em andamento para transferir alguns dos estágios de suporte para o componente Build. Esse será o build de vários estágios e a primeira versão já está disponível!