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:
-
MAJORindica alteração incompatível de API pública -
MINORindica adição de funcionalidade compatível -
PATCHindica 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 é deMAJOR. Se eu adiciono um novo método a uma API que não quebra ninguém, a mudança é deMINOR. Se eu corrijo um bug internalmente sem alterar a interface, a mudança é dePATCH.
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 -
RepositoryUrleRepositoryType— ajuda na navegação e rastreabilidade Além disso, vale empacotar símbolos quando a biblioteca é relevante para depuração e diagnósticos. Odotnet packtambém consegue incluir arquivos do pacote, comoREADME.mdeLICENSE, 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
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.orgcomo ambiente para testar a publicação antes donuget.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
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.Configversionado. 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));
}
}
📂 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>
A geração do pacote acontece em Release:
dotnet pack src/BlogSamples/Packaging/NuGetPublish/Contoso.TextRules/Contoso.TextRules.csproj -c Release
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
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
.nupkgem 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
PATCHou 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,
MAJORrepresenta mudança incompatível,MINORadiciona funcionalidade compatível ePATCHcorrige defeito compatível; pré-releases usam um sufixo como-beta.1. -
NuGetVersion — o NuGet normaliza versões como
1,1.0e1.0.0como equivalentes e suporta recursos do SemVer 2 em clientes modernos. -
Feed local — consumir o
.nupkgem 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)