Gerenciar atualizações de dependência no projeto do .NET

Concluído

Mais cedo ou mais tarde, você desejará atualizar para uma nova versão de uma biblioteca. Talvez uma função tenha sido marcada como preterida ou há um novo recurso em uma versão posterior de um pacote que você está usando.

Leve estas considerações em conta antes de tentar atualizar uma biblioteca:

  • O tipo de atualização: que tipo de atualização está disponível? Trata-se de uma pequena correção de bug? Ela adicionará um novo recurso de que você precisa? Isso interromperá seu código? Você pode comunicar o tipo de atualização usando um sistema chamado controle de versão semântico. A forma como o número da versão da biblioteca é expresso comunica aos desenvolvedores o tipo de atualização com a qual estão lidando.
  • Meu projeto está configurado corretamente?: você pode configurar o projeto do .NET para receber apenas os tipos de atualizações desejados. Você executará uma atualização apenas se um tipo específico de atualização estiver disponível. Recomendamos essa abordagem, pois você não corre o risco de se deparar com surpresas.
  • Problemas de segurança: gerenciar as dependências do projeto ao longo do tempo envolve estar ciente dos problemas que podem ocorrer. Problemas surgem conforme vulnerabilidades são detectadas, por exemplo. Idealmente, serão lançados patches que você poderá baixar. A ferramenta .Net Core ajuda você a executar uma auditoria de suas bibliotecas para descobrir se há pacotes que precisam ser atualizados. Ela também ajuda a adotar as medidas adequadas para corrigir problemas.

Usar o controle de versão semântico

Existe um padrão do setor chamado de controle de versão semântico, que é como fazer para expressar o tipo de alteração que você ou outro desenvolvedor está introduzindo em uma biblioteca. O controle de versão semântico funciona garantindo que um pacote tenha um número de versão e que o número de versão esteja dividido nestas seções:

  • Versão principal: o número mais à esquerda. Por exemplo, é o 1 em 1.0.0. Uma alteração nesse número significa que você pode esperar alterações da falha no código. Talvez seja necessário reescrever parte do código.
  • Versão secundária: o número do meio. Por exemplo, é o 2 em 1.2.0. Uma alteração nesse número significa que recursos foram adicionados. O código deve continuar funcionando. De modo geral, é seguro aceitar a atualização.
  • Versão do patch: o número mais à direita. Por exemplo, é o 3 em 1.2.3. Uma alteração nesse número significa que foi aplicada uma alteração que corrige algo no código que deveria ter funcionado. Deve ser seguro aceitar a atualização.

Esta tabela ilustra como o número de versão muda para cada tipo de versão:

Tipo O que acontece
Versão principal 1.0.0 muda para 2.0.0
Versão secundária 1.1.1 muda para 1.2.0
Versão de patch 1.0.1 muda para 1.0.2

Muitas empresas e desenvolvedores adotaram esse sistema. Se você pretende publicar pacotes e efetuar push deles para o Registro do NuGet, deve seguir o controle de versão semântico, como esperado. Mesmo que apenas baixe pacotes do registro do NuGet, você pode esperar que esses pacotes sigam o controle de versão semântico.

As alterações em um pacote podem introduzir riscos, incluindo o risco de um bug danificar sua empresa. Alguns riscos podem exigir que você reescreva parte do seu código. Reescrever código leva tempo e custa dinheiro.

Abordagem de atualização

Como desenvolvedor .NET, você pode informar ao .NET o comportamento de atualização que deseja. Pense na atualização em termos do risco. Aqui estão algumas abordagens:

  • Versão principal: estou confortável em atualizar para a versão principal mais recente assim que ela for lançada. Aceito o fato de que posso precisar alterar o código na minha extremidade.
  • Versão secundária: estou confortável com a adição de um novo recurso. Não estou confortável com interrupções de código.
  • Versão de patch, estou confortável apenas com correções de bugs.

Se estiver gerenciando um projeto novo ou menor do .NET, você poderá ser mais flexível ao definir a estratégia de atualização. Por exemplo, você pode sempre atualizar para a última versão. Em projetos mais complexos, há mais nuances, mas deixaremos isso para um módulo futuro.

De modo geral, quanto menor for a dependência que você estiver atualizando, menos dependências ela terá e maior será a probabilidade de que o processo de atualização seja fácil.

Configurar o arquivo de projeto para atualização

Ao adicionar uma ou mais dependências, pense em configurar o arquivo de projeto para obter um comportamento previsível ao restaurar, compilar ou executar o projeto. Você pode comunicar a abordagem que deseja adotar para um pacote. O NuGet tem os conceitos de intervalos de versão e versões flutuantes.

Vamos falar primeiro sobre os intervalos de versão. Essa é uma notação especial usada para especificar intervalos de versão específicos que você deseja resolver.

Notation Regra aplicada Descrição
1.0 x >= 1.0 Versão mínima, inclusiva
(1.0,) x > 1.0 Versão mínima, exclusiva
[1.0] x == 1.0 Correspondência exata da versão
(,1.0] x ≤ 1.0 Versão máxima, inclusiva
(,1.0) x < 1.0 Versão máxima, exclusiva
[1.0,2.0] 1.0 ≤ x ≤ 2.0 Intervalo exato, inclusivo
(1.0,2.0) 1.0 < x < 2.0 Intervalo exato, exclusivo
[1.0,2.0) 1.0 ≤ x < 2.0 Versão mínima inclusiva e máxima exclusiva combinadas
(1.0) inválido inválido

O NuGet também é compatível com o uso de uma notação de versão flutuante para as partes de sufixo principal, secundária, de patch e de pré-lançamento do número da versão. Essa notação é um asterisco (*). Por exemplo, a especificação de versão 6.0.* diz "usar a versão mais recente do 6.0.x". Em outro exemplo, 4.* significa "usar a versão mais recente do 4.x". O uso de uma versão flutuante reduz as alterações no arquivo de projeto, mantendo a versão mais recente de uma dependência.

Observação

É recomendável instalar uma versão específica em vez de usar qualquer uma das notações flutuantes. Instalar uma versão específica garante que seus builds possam ser repetidos, a menos que você solicite explicitamente a atualização de uma dependência.

Quando você está usando uma versão flutuante, o NuGet resolve a versão mais recente de um pacote que corresponde ao padrão de versão. No exemplo a seguir, 6.0.* obtém a versão mais recente de um pacote que começa com 6.0. Essa versão é a 6.0.1.

Diagram showing choosing the latest version when a floating version is requested.

Aqui estão alguns exemplos que você pode configurar para a versão principal, secundária ou de patch:

<!-- Accepts any version 6.1 and later. -->
<PackageReference Include="ExamplePackage" Version="6.1" />

<!-- Accepts any 6.x.y version. -->
<PackageReference Include="ExamplePackage" Version="6.*" />
<PackageReference Include="ExamplePackage" Version="[6,7)" />

<!-- Accepts any later version, but not including 4.1.3. Could be
     used to guarantee a dependency with a specific bug fix. -->
<PackageReference Include="ExamplePackage" Version="(4.1.3,)" />

<!-- Accepts any version earlier than 5.x, which might be used to prevent pulling in a later
     version of a dependency that changed its interface. However, we don't recommend this form because determining the earliest version can be difficult. -->
<PackageReference Include="ExamplePackage" Version="(,5.0)" />

<!-- Accepts any 1.x or 2.x version, but not 0.x or 3.x and later. -->
<PackageReference Include="ExamplePackage" Version="[1,3)" />

<!-- Accepts 1.3.2 up to 1.4.x, but not 1.5 and later. -->
<PackageReference Include="ExamplePackage" Version="[1.3.2,1.5)" />

Localizar e atualizar pacotes desatualizados

O comando dotnet list package --outdated lista pacotes desatualizados. Esse comando pode ajudar você a descobrir quando versões mais novas de pacotes estão disponíveis. Veja uma saída típica do comando:

Top-level Package      Requested   Resolved   Latest
> Humanizer            2.7.*       2.7.9      2.8.26

Estes são os significados dos nomes das colunas na saída:

  • Requested: a versão ou o intervalo de versão que você especificou.
  • Resolved: a versão real que foi baixada para o projeto que corresponde à versão especificada.
  • Latest: a última versão disponível para atualização do NuGet.

O fluxo de trabalho recomendado é executar os seguintes comandos, nesta ordem:

  1. Execute dotnet list package --outdated. Esse comando lista todos os pacotes desatualizados. Ele fornece informações nas colunas Requested, Resolved e Latest.
  2. Execute dotnet add package <package name>. Se você executar esse comando, ele tentará atualizar para a última versão. Opcionalmente, passe --version=<version number/range>.