DEV Community

Cover image for Como Publicar uma Biblioteca .NET no NuGet.org
Lincoln Zocateli
Lincoln Zocateli

Posted on Originally published at zocate.li

Como Publicar uma Biblioteca .NET no NuGet.org

Introdução

Publicar uma biblioteca .NET no NuGet.org não é apenas empacotar um projeto e enviar o .nupkg para o servidor. O ponto mais importante é tratar a API pública como um contrato que outras pessoas e outros times vão consumir. Se eu publicar uma API sem pensar em compatibilidade, o problema não fica só no pacote: ele se espalha para dependências, pipelines e aplicações que dependem do mesmo assembly. Por isso, a publicação segura combina três pilares: contrato estável, versionamento consciente e validação real do artefato em um ambiente de consumidor separado.

A ideia deste artigo é explicar a jornada completa de uma biblioteca .NET: preparar a API, decidir o tipo de distribuição, validar localmente, publicar em um ambiente de teste e, por fim, divulgar a versão estável em nuget.org. Também vou mostrar quando o pacote deve ficar em um feed público e quando ele precisa ir para Azure Artifacts ou GitHub Packages. O objetivo é dar uma visão prática para quem está publicando o primeiro pacote ou tentando organizar a distribuição interna da empresa.

Se você já usou pacotes NuGet em projetos de aplicação, sabe que o fluxo de consumo parece simples na superfície: restaurar, compilar e executar. O ponto delicado é o contrato. A biblioteca que parece compatível no build pode gerar erros de runtime quando o pacote é consumido em um projeto externo com metadados ou dependências diferentes. Por isso, vale separar a publicação em etapas: feed local, ambiente de teste do NuGet, publicação pública e feeds privados. O percurso é simples de entender, mas a disciplina é o que evita retrabalho e quebrar clientes.

⚠️ Atenção: o ambiente de teste do NuGet não substitui um feed privado. Ele serve para validar o fluxo de publicação antes de subir a versão definitiva. Azure Artifacts e GitHub Packages existem para distribuição interna persistente e autenticada.

Pré-requisitos

Antes de empacotar o primeiro pacote, eu gosto de validar alguns preceitos básicos para evitar a sensação de que a publicação foi bem-sucedida e, depois, o pacote não ficou útil para quem vai consumir. Em primeiro lugar, você precisa do .NET SDK instalado e funcional; a checagem mínima é dotnet --info. O lado operacional também importa: uma conta no nuget.org e uma API key com escopo limitado para o pacote. Isso reduz o impacto de vazamento ou uso indevido.

O próximo passo é separar o cenário de distribuição. Se o pacote é público, um fluxo de publicação no nuget.org exige revisão e indexação. Se ele é interno, a melhor alternativa costuma ser Azure Artifacts ou GitHub Packages, com autenticação e controle de acesso. Em qualquer caso, o ideal é nunca expor tokens em arquivos versionados ou em comandos de terminal que fiquem no histórico do shell.

Também vale deixar claro que esse processo não é só de “empacotar e publicar”. É de “empacotar e validar”. A rotina de publicação tem um exercício importante: criar um projeto consumidor separado e instalar o pacote que acabou de ser gerado. Só assim é possível verificar se README, dependências, arquivos e metadados estão corretos.

Defina o Contrato e a Estratégia de Versionamento SemVer

A publicação de bibliotecas .NET exige que o versionamento seja tratado como parte do contrato público. O SemVer é a convenção mais usada no ecossistema e define que a versão siga a estrutura MAJOR.MINOR.PATCH. Cada componente comunica algo diferente:

  • MAJOR indica alteração incompatível de API pública
  • MINOR indica adição de funcionalidade compatível
  • PATCH indica correção de defeito sem mudar a API Não é só uma convenção estética. O número da versão comunica ao consumidor o risco da atualização. Se eu remover um método público ou mudar o comportamento de forma que um cliente dependa do antigo contrato, a mudança é de MAJOR. Se eu adiciono um novo método a uma API que não quebra ninguém, a mudança é de MINOR. Se eu corrijo um bug internalmente sem alterar a interface, a mudança é de PATCH.

Eu também gosto de reforçar a importância das versões pré-release. Em bibliotecas, 1.5.0-beta.1, 1.5.0-rc.1 e 1.6.0-alpha.3 são úteis para validar uma funcionalidade antes de o pacote ser publicado como estável. Isso reduz o risco de anunciar uma versão definitiva sem testar os impactos reais. A regra mais importante é simples: uma versão publicada é imutável. Se ela estiver errada, você corrige em uma nova versão, não reescreve a antiga.

Mudança no contrato público Próxima versão Exemplo
Remover método ou alterar comportamento incompatível MAJOR 1.4.2 → 2.0.0
Adicionar método sem quebrar consumidores MINOR 1.4.2 → 1.5.0
Corrigir defeito sem alterar contrato PATCH 1.4.2 → 1.4.3
Validar versão futura Sufixo pré-release 1.5.0-beta.1

💡 Dica: o chamado SemVer 2 também permite pré-releases com rótulos e regras de normalização que o NuGet aceita. Isso é importante quando você quer testar uma nova funcionalidade com consumidores que ainda não querem depender dela em produção.

Prepare a Biblioteca para Distribuição

Uma biblioteca pode ser pequena e ainda assim precisa de itens mínimos de qualidade para ser publicada. O projeto de exemplo usado neste artigo está no repositório de exemplos do blog e chama-se Contoso.TextRules. Ele expõe regras reutilizáveis para validação e normalização de texto. A estrutura do pacote é simples, mas a forma como ele é descrito no .csproj é o que permite que o NuGet gere uma página útil no site e que o pacote seja reconhecido corretamente pelo tooling do .NET.

No arquivo de projeto, alguns metadados são fundamentais:

  • PackageId — identifica o pacote
  • Version — define a versão da distribuição
  • Authors — informa autoria
  • Description — resumo útil para busca e descoberta
  • PackageTags — pesquisa e classificação no NuGet.org
  • PackageReadmeFile — inclui README dentro do pacote
  • PackageLicenseExpression — licença SPDX para transparência
  • RepositoryUrl e RepositoryType — ajuda na navegação e rastreabilidade Além disso, vale empacotar símbolos quando a biblioteca é relevante para depuração e diagnósticos. O dotnet pack também consegue incluir arquivos do pacote, como README.md e LICENSE, o que melhora bastante a experiência de quem vai consumir a biblioteca.

A separação entre abstração e implementação também é relevante como boa prática. O NuGet tem uma cultura muito forte de pacotes pequenos e de responsabilidade clara. Em projetos maiores, é comum separar um contrato da implementação: por exemplo, projetar uma biblioteca de abstração com interfaces e contratos e outra biblioteca com a implementação concreta. Isso evita que consumidores dependam de todo o conjunto de funcionalidades e permite que a API pública evolua com mais clareza. Isso não é obrigatório para todo projeto pequeno, mas é um padrão útil para bibliotecas de plataforma ou componentes de domínio.

📂 Código Fonte: O exemplo completo está disponível no repositório de exemplos do blog:
BlogSamples/Packaging/NuGetPublish/

Empacote e Valide Localmente Antes do Push

Depois de preparar a biblioteca, o próximo passo é empacotar o artefato em modo Release. Isso é o ponto em que eu verifico se o projeto realmente gera um pacote completo. O comando principal é:

dotnet pack src/BlogSamples/Packaging/NuGetPublish/Contoso.TextRules/Contoso.TextRules.csproj -c Release
Enter fullscreen mode Exit fullscreen mode

O resultado esperado é um .nupkg na pasta bin/Release. Mas o simples fato de existir um arquivo não basta. Eu sempre inspeciono o pacote para confirmar que ele contém o README, a licença e as dependências corretas. O pacote pode compilar e ainda assim ter metadados incompletos ou arquivos faltando, o que costuma aparecer quando a biblioteca é consumida por um projeto externo.

A validação mais importante é consumir o pacote em outro projeto. Isso muda o cenário: o consumidor não está dentro do mesmo repositório nem do mesmo assembly. Ele instala o pacote via dotnet add package ou via feed local, e aí aparecem erros de integração que os testes da própria biblioteca não capturam. Um erro clássico é um arquivo que fica ausente no pacote ou um README que não foi incluído corretamente. O pacote precisa funcionar como um item distribuído, não apenas como um projeto compilado localmente.

Valide no Ambiente de Teste do NuGet

Depois que o pacote está funcionando localmente, o próximo ambiente natural é o teste do NuGet. O endereço oficial é https://int.nugettest.org. Esse ambiente foi criado para validar o fluxo de publicação antes do envio ao feed público. O objetivo não é substituir o processo de produção, e sim confirmar que o pacote pode ser enviado, restaurado e consumido em um cenário semelhante ao real.

Em geral, o fluxo é simples: configurar uma fonte de pacote nomeada para o ambiente de teste, autenticar com o usuário e enviar a versão. Com o pacote em mãos, um projeto consumidor diferente pode restaurá-lo e executar o código real. Essa validação reduz o risco de uma falha de packaging ou de uma API pública que ainda não está pronta para o feed público.

ℹ️ Informação: a documentação oficial do NuGet usa int.nugettest.org como ambiente para testar a publicação antes do nuget.org. Pacotes enviados para esse ambiente podem não ser preservados, por isso ele é útil para validação, não para distribuição persistente.

Publique uma Versão Estável no NuGet.org

Quando a API pública está pronta e o pacote passou pela validação de consumo, a próxima etapa é publicar a versão estável em nuget.org. Esse processo exige uma API key do NuGet com escopo mínimo: o ideal é restringir o token ao prefixo do pacote e ao tempo de validade necessário. Isso reduz muito o impacto de vazamento de credenciais.

O comando mais comum é o dotnet nuget push, geralmente com o pacote já empacotado em Release:

dotnet nuget push ./artifacts/Contoso.TextRules.1.0.0.nupkg --api-key $NUGET_API_KEY --source https://api.nuget.org/v3/index.json
Enter fullscreen mode Exit fullscreen mode

Na prática, a publicação no nuget.org não é o fim do processo: ela também dispara a indexação e a disponibilidade do pacote para busca e restauração. O pacote passa por validações do NuGet e, em alguns casos, demora alguns minutos para aparecer como disponível para instalação. Isso é normal e faz parte do fluxo oficial. Também vale deixar claro que a decisão de usar unlist faz sentido para remover uma versão da visibilidade pública sem apagá-la por completo; já “excluir” uma versão não é a abordagem recomendada para distribuir um artefato público de forma segura.

A regra mais importante é reservar a primeira estável 1.0.0 para o momento certo. Em bibliotecas, eu prefiro validar a API em pré-release primeiro. Isso dá tempo de ajustar comportamento sem a pressão de “uma versão potencialmente quebrada” já disponível publicamente.

Distribua Bibliotecas Internas com Azure Artifacts e GitHub Packages

Nem todo pacote merece ir para nuget.org. Quando a biblioteca é interna, um feed privado é a opção mais segura e fácil de operar. Azure Artifacts e GitHub Packages atendem a esse cenário com autenticação e controle de acesso. A decisão geralmente depende da assinatura da plataforma usada pela organização.

Azure Artifacts

O Azure Artifacts funciona muito bem quando a organização já usa Azure DevOps e tem pipelines centralizados. O feed de pacotes fica associado à organização e ao projeto do Azure DevOps, e a autenticação pode ser integrada a pipelines e regras de segurança. Para times usando Azure como plataforma principal, essa é uma forma natural de publicar bibliotecas internas com políticas de acesso e revisão de versões.

GitHub Packages

O GitHub Packages se encaixa bem em times que já vivem dentro do GitHub. A autenticação usa token com escopo e permissões mínimas, e a integração com GitHub Actions costuma ser direta. Isso facilita muito a publicação de componentes internos, bibliotecas compartilhas e dependências para repositórios privados e públicos em diferentes fluxos de desenvolvimento.

Critério Azure Artifacts GitHub Packages
Plataforma principal Azure DevOps GitHub
Integração de CI Azure Pipelines GitHub Actions
Controle de acesso Organização e projeto Organização e repositório
Melhor cenário Times centralizados no Azure DevOps Times que já usam GitHub

⚠️ Atenção: nunca deixe tokens em NuGet.Config versionado. O arquivo de configuração pode apontar para diferentes fontes, mas os segredos devem sair do repositório e entrar por variáveis de ambiente ou secret stores do CI.

Automatize Releases sem Quebrar Consumidores

A automação de publicação é importante, mas ela precisa ser um guard rail, não um atalho. O fluxo ideal de CI/CD para uma biblioteca .NET é: validar testes, empacotar, verificar se a versão já existe no feed e só então publicar. Isso evita que uma tentativa de push reescreva uma versão já publicada ou publique um pacote quebrado para o feed errado.

Uma boa estratégia é separar release e publicação em dois momentos. O processo de tag e changelog fica em Git; a publicação de pré-release pode ir para feed privado ou ambiente de teste; a versão estável vai para nuget.org somente após revisão de compatibilidade. Isso ajuda a manter o controle da API pública e faz com que a decisão de publicar uma nova versão seja uma decisão mais consciente.

Em pipelines, a regra é proteger os segredos. O principal é manter o token em um secret do CI, usá-lo em um passo específico de publish e restringir as permissões. O código da biblioteca pode ser automático, mas a decisão de liberar uma nova versão estável continua sendo algo que exige atenção humana e documentação de breaking change.

Exemplo Prático

Vou usar o mesmo cenário do projeto de exemplo: uma biblioteca chamada Contoso.TextRules com regras de validação reutilizáveis. O objetivo é demonstrar a parte do contrato: a API pública fica estável, a versão de pacote inicia como 1.0.0-alpha.1, e a promoção para 1.0.0 acontece depois que a biblioteca é validada em um ambiente de consumidor separado.

Primeiro, a biblioteca em si. O trecho principal mostra como a API pública é simples e previsível:

namespace BlogSamples.Packaging.NuGetPublish;

public sealed class TextRuleSet
{
    public bool IsValidEmail(string value)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(value);

        return value.Contains('@')
            && value.Contains('.')
            && value.IndexOf('@', StringComparison.Ordinal) > 0;
    }

    public string NormalizeWhitespace(string value)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(value);

        return string.Join(' ', value.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
    }
}
Enter fullscreen mode Exit fullscreen mode

📂 Código Fonte: O exemplo completo está disponível no repositório de exemplos do blog:
BlogSamples/Packaging/NuGetPublish/Contoso.TextRules/

Em seguida, acrescento os metadados importantes para o pacote:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <PackageId>Contoso.TextRules</PackageId>
    <Version>1.0.0-alpha.1</Version>
    <PackageReadmeFile>README.md</PackageReadmeFile>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <RepositoryUrl>https://github.com/lzocateli/blog-zocateli-sample</RepositoryUrl>
    <RepositoryType>git</RepositoryType>
  </PropertyGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

A geração do pacote acontece em Release:

dotnet pack src/BlogSamples/Packaging/NuGetPublish/Contoso.TextRules/Contoso.TextRules.csproj -c Release
Enter fullscreen mode Exit fullscreen mode

Para consumo em um projeto separado, eu também gosto de configurar uma fonte local ou de teste e instalar o pacote em um consumidor mínimo:

dotnet new classlib -n Contoso.TextRules.Consumer
cd Contoso.TextRules.Consumer

dotnet add package Contoso.TextRules --version 1.0.0-alpha.1 --source /caminho/para/o/feed
Enter fullscreen mode Exit fullscreen mode

Esse passo é essencial. Ele confirma que a biblioteca não depende apenas do projeto em que ela foi compilada, mas do artefato que será entregue ao consumidor final. Depois da validação, a promoção para 1.0.0 fica muito mais segura e a decisão de publicar em nuget.org passa a ser uma escolha tecnológica, não só uma sensação de “está funcionando”.

Dicas e Boas Práticas

  • Trate a API pública como contrato. Qualquer alteração de assinatura, comportamento observável ou semântica documentada precisa ser avaliada pelo impacto em consumidores antes de escolher a versão SemVer.
  • Consuma o .nupkg em outro projeto. Testar apenas a biblioteca compilada não verifica se o pacote contém README, dependências e assemblies corretos.
  • Crie API keys e tokens com escopo mínimo. Uma credencial limitada ao prefixo do pacote e à operação de push reduz o impacto de vazamento acidental.
  • Nunca reutilize uma versão publicada. Corrija o problema em uma nova versão PATCH ou publique uma nova pré-release; isso preserva reprodutibilidade de restore.
  • Separe fontes públicas e privadas no NuGet.Config. Dê nomes claros às fontes, restrinja mapeamento de pacotes quando apropriado e evite que dependências internas sejam procuradas no nuget.org.
  • Automatize, mas preserve revisão. O CI deve garantir testes e empacotamento repetíveis; a decisão de uma release estável ainda exige revisão de compatibilidade e changelog.

Resumo Objetivo

  • SemVer — em uma biblioteca .NET, MAJOR representa mudança incompatível, MINOR adiciona funcionalidade compatível e PATCH corrige defeito compatível; pré-releases usam um sufixo como -beta.1.
  • NuGetVersion — o NuGet normaliza versões como 1, 1.0 e 1.0.0 como equivalentes e suporta recursos do SemVer 2 em clientes modernos.
  • Feed local — consumir o .nupkg em um projeto separado verifica o artefato distribuído, incluindo dependências, metadados e arquivos anexados.
  • int.nugettest.org — é o ambiente de teste documentado pelo NuGet para validar publicação antes do nuget.org, mas os pacotes podem não ser preservados.
  • nuget.org — exige uma versão exata no pacote e valida o upload antes de indexá-lo; API keys devem ter escopo e expiração mínimos.
  • Feeds privados — Azure Artifacts e GitHub Packages distribuem bibliotecas internas com autenticação e controle de acesso, função diferente da homologação no ambiente de teste público.

Leia Também

  • C# como Script no .NET — usar bibliotecas e pacotes NuGet em scripts C#.
  • EFCore.BulkExtensions: Operações em Massa no .NET — exemplo de biblioteca distribuída pelo ecossistema NuGet.
  • CI/CD Seguro: Dependabot, SAST e DAST no GitHub — proteger dependências e pipelines de publicação.
  • Configuração .NET 8+: IOptions, Secrets e Docker — administrar segredos e configuração em aplicações .NET.

Referências

  • How to publish NuGet packages — documentação oficial para publicação, API keys, indexação e ambiente de teste.
  • NuGet Package Version Reference — regras de versão, pré-releases, normalização e SemVer 2.
  • Overview of Hosting Your Own NuGet Feeds — alternativas para distribuição privada de pacotes.
  • Semantic Versioning 2.0.0 — especificação do contrato de versionamento semântico.
  • Azure Artifacts documentation — publicação e consumo de pacotes NuGet no Azure DevOps.
  • Working with the NuGet registry — configuração e publicação no GitHub Packages.
  • Nuuvify.CommonPack — exemplo real de coleção de bibliotecas .NET publicada no NuGet, organizada por responsabilidade e projetos de abstração. 📬

👉 Artigo completo com todos os exemplos de código: Como Publicar uma Biblioteca .NET no NuGet.org

Top comments (0)