← AZ-400: DevOps da entrega à operação
10 / 26 · 100 MIN

Branches, revisão e recuperação: controlar a alteração final

Decide com base no diff, na identidade do controlo e no histórico Git; ensaia recuperação e backports num repositório temporário.

1. Definir quem revê cada alteração

Num projeto fictício, uma aplicação de pagamentos recebe uma alteração de timeout perto da janela de produção. Ana abriu o PR, Bruno acrescentou o último commit e uma aprovação aparece no ecrã. Antes de concluir que a revisão terminou, identifica quem aprovou, que diff estava disponível e quais as condições da policy. No Azure Repos, permitir o voto do autor e excluir o último pusher são decisões distintas. No exemplo, Ana pode rever a mudança de Bruno se as outras condições o permitirem. O PM deve conseguir explicar o critério sem depender da cor do indicador. Regista o objetivo do controlo: competência técnica, separação de responsabilidades ou revisão da nova iteração. Um número de aprovações escolhido sem esse objetivo pode atrasar trabalho sem melhorar a decisão. Define também quem responde quando o revisor habitual está ausente durante uma intervenção fora de horas.

2. Ligar a revisão à origem e a validação à base

A equipa precisa de preservar uma objeção sobre timeouts, mas retirar aprovações quando chega código novo. Reset all approval votes conserva votos de rejeição ou espera; Reset all code reviewer votes também os remove. Escolhe a opção de acordo com o comportamento pretendido, não pela semelhança dos nomes. Existe ainda outro eixo: a branch de destino pode mudar sem qualquer push na origem. No exercício, o adaptador passou às 10:00, mas outra equipa alterou a interface às 10:08. Para exigir nova validação imediatamente, configura a expiração correspondente à mudança da base. Discute o custo de execuções adicionais e a criticidade do contrato alterado. O resultado anterior continua a documentar o que foi ensaiado, mas a decisão atual precisa de evidência adequada à combinação atual. No relatório de mudança, distingue atualização do código, revisão humana e execução de testes.

3. Identificar o controlo que produziu o resultado

Um check chamado validate pode representar testes da aplicação ou apenas validação de documentação. Se dois workflows usam esse nome, a pessoa que analisa o PR precisa de resolver a ambiguidade antes de alterar requisitos. Usa nomes de jobs distintos e atualiza a seleção dos checks obrigatórios. Num segundo problema, risk-scan pode ser publicado por duas integrações, mas apenas RiskGate decide o controlo de risco. A seleção da aplicação esperada permite restringir a origem aceite. Estes dois problemas exigem perguntas diferentes: qual atividade foi executada e quem publicou a decisão? No handover, guarda o nome do controlo, a referência avaliada e o responsável pelo serviço que o produz. Um check aceite pela plataforma também não demonstra, por si só, que todos os ensaios funcionais do projeto existem. A cobertura precisa de ser discutida com quem conhece o comportamento da aplicação.

4. Tornar o ownership legível e aplicável

O ficheiro CODEOWNERS usado para um PR pertence à branch base. Se a proposta introduzir esse ficheiro apenas na origem, não assumas que já governa a seleção dos reviewers desse PR. Revê a configuração do destino. A última correspondência prevalece; para associar duas equipas ao mesmo padrão, coloca-as na mesma linha. Pedir revisão e exigir aprovação são configurações relacionadas mas diferentes. O exercício usa caminhos de pagamentos e equipas fictícias com acesso previamente confirmado. Pede ao aluno que desenhe uma pequena matriz: caminho alterado, regra aplicável, equipa responsável e requisito de aprovação. Acrescenta um exemplo de pipeline ou infraestrutura para detetar lacunas fora do código da aplicação. Esta matriz ajuda o PM a identificar dependências de especialistas, horários de disponibilidade e pontos em que duas equipas pensam que a outra é responsável. Testa a regra num PR representativo antes de a considerar operacional.

5. Avaliar a combinação que será integrada

Dois PRs podem passar separadamente e falhar quando usados em conjunto. No caso prático, um muda o produtor de eventos e outro muda o consumidor. Uma merge queue cria uma combinação a validar com a base e alterações anteriores da fila. Com GitHub Actions, o workflow precisa de responder a merge_group; o resultado do PR isolado não substitui essa execução. Quando o check não aparece, procura primeiro se existe execução para a referência correta. Distingue ausência de execução, falha técnica do runner e falha funcional do teste. A resposta operacional muda em cada caso. Durante uma janela curta, atribui um responsável pela correção do trigger e um limite temporal para decidir o adiamento. Publicar sucesso manual com base noutro resultado esconderia a lacuna. Explica ao negócio qual risco continua sem evidência e quando poderá existir uma nova decisão fundamentada.

6. Ler o grafo antes de reverter um merge

Um merge tem mais de um parent. Em git revert -m, o número identifica o parent usado como referência, não a pessoa ou branch que se quer culpar. No laboratório, o primeiro parent é main antes da integração; confirma-se essa relação antes de reverter. Observa depois uma diferença importante: o ficheiro acrescentado desaparece, mas o commit da feature continua ancestral de main. Acrescentar apenas documentação à feature e integrar novamente não restaura automaticamente o ficheiro removido. O exercício permite observar o conteúdo e o grafo lado a lado. Uma eventual reintrodução deve ser uma decisão explícita, com o conjunto de alterações pretendido. O laboratório reverte a alteração inversa num grafo controlado; isso não é uma receita universal para produção. Uma recuperação real pode ter de tratar dados, mensagens já emitidas e configurações externas que Git não representa.

7. Recuperar um candidato sem alterar a referência partilhada

No segundo exercício, um commit de experiência deixa de estar numa branch depois de regressar a main. O HEAD reflog local ainda indica o identificador e o objeto existe. Criar uma branch rescue nesse identificador preserva um caminho para inspecionar o candidato, sem mover main. O reflog pertence ao repositório local e pode expirar; não o trates como cópia de segurança universal. Confirma o conteúdo antes de o promover através do processo normal de revisão. O laboratório verifica que rescue aponta para o candidato e que main conserva o valor anterior. Para trabalho em equipa, regista como foi identificado o commit e o que falta validar. O identificador observado prova que se encontrou um objeto, não que esse objeto resolve o incidente. Se a pista tiver desaparecido, a investigação depende das restantes cópias e políticas de retenção disponíveis no contexto real.

8. Fechar o ciclo com um backport ensaiado

Uma correção em main pode ser necessária numa versão de manutenção que tem outro parent e outras dependências. Um cherry-pick sem conflitos não demonstra compatibilidade funcional. No laboratório, -x acrescenta a referência à origem no caso sem conflitos; o novo commit tem uma identidade diferente. Examina o diff resultante e define testes adequados à versão de destino. Se ocorrer conflito noutro exercício, --abort permite cancelar a sequência; não escolhas uma resolução apenas para eliminar marcadores. O código abaixo cria um repositório temporário, sem remotes, executa dezoito verificações e remove esse repositório ao terminar. Requer Python e Git instalados. Antes de executar, prevê quais os ficheiros presentes após cada integração. Depois compara as previsões com as verificações. Termina escrevendo um handover curto: referência de origem, referência aplicada, evidência de compatibilidade e limitações da recuperação. Este registo deve ser compreensível por quem assumir o suporte seguinte.

# Original local Git exercise. Creates only a temporary repository; no remotes.
import os
from pathlib import Path
import subprocess
import tempfile

def run_lab():
    env = {k: v for k, v in os.environ.items() if not k.startswith('GIT_')}
    env.update(GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL=os.devnull,
               GIT_TERMINAL_PROMPT='0', GIT_EDITOR='true', LC_ALL='C')
    with tempfile.TemporaryDirectory(prefix='dr-az400-git-') as folder:
        root = Path(folder)
        def git(*args):
            return subprocess.run(
                ['git', '-c', 'core.hooksPath=' + os.devnull,
                 '-c', 'commit.gpgSign=false', *args],
                cwd=root, env=env, check=True, text=True,
                capture_output=True, timeout=15).stdout.strip()
        def commit_file(name, text, message):
            (root / name).write_text(text)
            git('add', '--', name)
            git('commit', '-m', message)
            return git('rev-parse', 'HEAD')
        git('init', '--initial-branch=main', '--template=')
        git('config', 'user.name', 'DR local exercise')
        git('config', 'user.email', 'exercise@example.invalid')
        base = commit_file('service.txt', 'version=1\n', 'Initial service')
        git('switch', '-c', 'feature')
        feature = commit_file('rules.csv', 'limit,100\n', 'Add fictional rule')
        git('switch', 'main')
        before_merge = commit_file('audit.txt', 'audit=enabled\n', 'Add audit setting')
        git('merge', '--no-ff', 'feature', '-m', 'Integrate fictional rule')
        merge = git('rev-parse', 'HEAD')
        assert git('show', '-s', '--format=%P', merge).split() == [before_merge, feature]
        assert (root / 'rules.csv').read_text() == 'limit,100\n'
        git('revert', '--no-edit', '-m', '1', merge)
        reversal = git('rev-parse', 'HEAD')
        assert not (root / 'rules.csv').exists()
        assert (root / 'audit.txt').read_text() == 'audit=enabled\n'
        assert git('merge-base', '--is-ancestor', feature, 'main') == ''
        git('switch', 'feature')
        commit_file('README.txt', 'Rule documentation updated\n', 'Document the rule')
        git('switch', 'main')
        git('merge', '--no-ff', 'feature', '-m', 'Integrate later documentation')
        assert (root / 'README.txt').read_text() == 'Rule documentation updated\n'
        assert not (root / 'rules.csv').exists()
        # Only for this controlled graph: restore the entire reverted change.
        # A production recovery needs review of data effects and compatibility.
        git('revert', '--no-edit', reversal)
        assert (root / 'rules.csv').read_text() == 'limit,100\n'
        assert (root / 'README.txt').read_text() == 'Rule documentation updated\n'
        stable = git('rev-parse', 'main')
        git('switch', '--detach', 'main')
        candidate = commit_file('candidate.txt', 'inspect before adoption\n', 'Recovery candidate')
        git('switch', 'main')
        assert candidate in git('reflog', 'show', '--format=%H', 'HEAD').splitlines()
        git('branch', 'rescue', candidate)
        assert git('rev-parse', 'rescue') == candidate
        assert git('rev-parse', 'main') == stable
        assert git('show', 'rescue:candidate.txt') == 'inspect before adoption'
        # Backport between divergent local branches; no conflict in this fixture.
        git('switch', '-c', 'maintenance', base)
        git('cherry-pick', '-x', feature)
        picked = git('rev-parse', 'HEAD')
        assert picked != feature
        assert feature in git('show', '-s', '--format=%B', picked)
        assert (root / 'rules.csv').read_text() == 'limit,100\n'
        assert git('status', '--porcelain') == ''
        assert git('remote') == ''
        print('18 local Git assertions passed; temporary repository removed on exit')

if __name__ == '__main__':
    run_lab()
NA PRÁTICA

O laboratório integra rules.csv, reverte o merge e integra apenas documentação posterior. O ficheiro continua ausente até uma reintrodução explícita. Depois recupera um commit por uma nova branch e ensaia um backport.

Armadilhas comuns

Contar votos inelegíveis; aceitar validação sobre uma base anterior; confundir nomes de checks; esperar por um evento não configurado; assumir que reverter apaga ancestralidade; tratar reflog como backup; confundir backport sem conflitos com compatibilidade.

Tópicos relacionados: Proveniência de artefactos · Aprovações de release · Resposta a incidentes · Testes de contratos

Leva esta ideia contigo

A integração depende de evidência sobre a alteração final. A recuperação depende de compreender o grafo, preservar referências úteis e avaliar os efeitos fora de Git.

Criar conta

Referência: Set and manage branch policies · AZ-400 objectives 2026-07-27

Microsoft é uma marca comercial do grupo de empresas Microsoft. A dr.pt é uma plataforma de preparação independente e não está afiliada, associada, patrocinada, autorizada nem aprovada por Microsoft. Os conteúdos e as perguntas são originais, não são perguntas oficiais de exame, e concluir os nossos testes não atribui nem garante qualquer certificação. Os nomes são usados apenas para identificar o tema. Todas as outras marcas pertencem aos respetivos titulares.