Como adicionar Source Link num pacote NuGet

Neste artigo, exploro em detalhes como adicionar suporte ao Source Link em uma biblioteca publicada no NuGet.

Como adicionar Source Link num pacote NuGet
5 min de leitura

Como pudemos ver no artigo anterior, o Source Link é uma tecnologia que visa prover uma grande experiência de depuração para binários .NET e que facilita muito a vida dos desenvolvedores na hora de resolver problemas.

Neste artigo, veremos como adicionar suporte ao Source Link nas nossas bibliotecas.

O primeiro passo para adicionar suporte ao Source Link nos nossos assemblies é instalar o pacote Source Link do provedor de controle de versão que você utiliza no seu projeto.

Para fins de exemplo neste artigo, vou utilizar a minha biblioteca ForeverFactory, que está hospedada no GitHub. Para uma lista completa dos provedores suportados, consulte o repositório do Source Link.

Assim, você pode instalar o SourceLink adicionando a seguinte referência no seu projeto:

Projeto.csproj
<ItemGroup>
  <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
</ItemGroup>
Note

É de suma importância que a propriedade PrivateAssets seja definida como all para o Source Link funcionar.

Feito isso, precisamos configurar no arquivo do projeto como será feita a geração dos pacotes de símbolos.

ForeverFactory.csproj
<PropertyGroup>
  <TargetFrameworks>net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
  <Features>strict</Features>
  <Authors>Dyego Alekssander Maas</Authors>
  <Copyright>Copyright Dyego Alekssander Maas</Copyright>
  <PackageId>ForeverFactory</PackageId>
  <Description>Forever Factory makes it super easy to build many customized objects.</Description>
  <PackageProjectUrl>https://github.com/DyegoMaas/ForeverFactory</PackageProjectUrl>
  <RepositoryUrl>https://github.com/DyegoMaas/ForeverFactory</RepositoryUrl>
  <PackageTags>ForeverFactory factory builder</PackageTags>
  <PackageLicenseExpression>MIT</PackageLicenseExpression>
  <MinVerTagPrefix>v</MinVerTagPrefix>
  <EmbedUntrackedSources>true</EmbedUntrackedSources>
  <PackageIcon>icon_128x128.png</PackageIcon>
  <DebugType>full</DebugType>

  <PublishRepositoryUrl>true</PublishRepositoryUrl>       <!-- 1 -->
  <EmbedUntrackedSources>true</EmbedUntrackedSources>     <!-- 2 -->
  <IncludeSymbols>true</IncludeSymbols>                   <!-- 3 -->
  <SymbolPackageFormat>snupkg</SymbolPackageFormat>       <!-- 4 -->
</PropertyGroup>
  1. Define a URL do repositório que será incluída nos metadados do pacote de símbolos
  2. Instrui o o sistema de build a embarcar arquivos que participaram do build, mas não são rastreados pelo controle de versão
  3. Instrui o sistema de build a gerar um pacote de símbolos, além do pacote com os binários
  4. Define o formato do pacote de símbolos como snupkg

Com essas configurações feitas, podemos gerar um pacote localmente com os seguintes comandos:

dotnet build -c Release
dotnet pack src/ForeverFactory/ForeverFactory.csproj -c Release --no-build -o nuget-package

Com este comando dotnet pack, geramos nosso pacote NuGet no diretório nuget-package, como podemos ver na imagem a seguir:

Diretório gerado, com os arquivos ForeverFactory.4.0.2.nupkg e ForeverFactory.4.0.2.snupkg
Diretório com os pacotes gerados

#Como validar pacotes NuGet usando NGPE

Antes de publicar nosso pacote, podemos validá-lo com a ferramenta NuGet Package Explorer (NGPE).

Arquivo ForeverFactory.4.0.2.nupkg aberto no NuGet Package Explorer, com os metadados à esquerda, e a árvore de conteúdos à direita
Pacote .nupkg aberto no NuGet Package Explorer

Com o NGPE, podemos confirmar que os metadados necessários foram embarcados no pacote:

  • O conteúdo do pacote, com os binários, ícone do projeto e XMLs de documentação
  • Na seção Repository, encontramos a URL do repositório, e o commit atual
  • Na seção Health, a ferramenta nos indica dois problemas:
    1. O pacote foi gerado a partir de um build não-determístico
    2. Há flags do compilador faltando
Important

Voltaremos a tratar desses dois pontos mais adiante no artigo. Vamos agora verificar o que contém o pacote de símbolos gerado:

Arquivo ForeverFactory.4.0.2.snupkg aberto no NuGet Package Explorer, com os metadados à esquerda, e a árvore de conteúdos à direita
Pacote .snupkg aberto no NuGet Package Explorer

Como podemos verificar no arquivo, o pacote de símbolos contém os arquivos PDB para depuração, além dos metadados associando repositório e commit ao pacote.

#Configurando builds determinísticos

Por padrão, os builds do .NET são indeterminísticos. Isso significa que cada build gera binários e símbolos ligeiramente diferentes de builds anteriores.

Eles são diferentes porque incluem metadados acerca da máquina que executou o build, timestamp, entre outros, localização dos arquivos na máquina e muito mais. Essas informações servem principalmente para facilitar a depuração local desses binários.

Isso é um problema para um pacote NuGet publicado porque não é possível garantir que este pacote é o que diz ser.

Important

A recomendação nesse caso, é fazer com que o build que publica o pacote nos servidores do NuGet, executado num pipeline de CI, execute builds determinísticos.

Esta configuração é feita através da

ForeverFactory.csproj
<PropertyGroup>
  <TargetFrameworks>net5.0;netstandard2.0;netstandard2.1</TargetFrameworks>
  <Features>strict</Features>
  <Authors>Dyego Alekssander Maas</Authors>
  <Copyright>Copyright Dyego Alekssander Maas</Copyright>
  <PackageId>ForeverFactory</PackageId>
  <Description>Forever Factory makes it super easy to build many customized objects.</Description>
  <PackageProjectUrl>https://github.com/DyegoMaas/ForeverFactory</PackageProjectUrl>
  <RepositoryUrl>https://github.com/DyegoMaas/ForeverFactory</RepositoryUrl>
  <PackageTags>ForeverFactory factory builder</PackageTags>
  <PackageLicenseExpression>MIT</PackageLicenseExpression>
  <MinVerTagPrefix>v</MinVerTagPrefix>
  <PackageIcon>icon_128x128.png</PackageIcon>
  <DebugType>full</DebugType>

  <PublishRepositoryUrl>true</PublishRepositoryUrl>
  <EmbedUntrackedSources>true</EmbedUntrackedSources>
  <IncludeSymbols>true</IncludeSymbols>
  <SymbolPackageFormat>snupkg</SymbolPackageFormat>
  
  <Deterministic>true</Deterministic> <!-- 1 -->
  <ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">true</ContinuousIntegrationBuild> <!-- 2 -->
</PropertyGroup>
  1. Instrui o sisema de build a utilizar builds determinísticos sempre que possível
  2. Determina que o build foi realizado num pipeline de integração contínua. Note que esta propriedade só é definida como true se o build for executado num workflow do GitHub Actions, conforme definido pela variável de ambiente GITHUB_ACTIONS estiver definida como true

Conforme explicado neste repositório, builds determinísticos só acontecem em builds de integração contínua.

Podemos simular um build de CI localmente com o seguinte comando:

dotnet build /p:ContinuousIntegrationBuild=true

Abrindo este novo build no NGPE, podemos verificar que este novo pacote foi gerado com um build determinístico.

Pacote aberto no NuGet Package Explorer confirmando que o build foi determístico
Build determinístico

A próxima etapa, é realizar a publicação do pacote. Para fins de exemplo, compartilho a seguir o pipeline de release do meu projeto ForeverFactory:

.github/workflows/release.yaml
name: Release
on:
push:
  tags:
  - '*.*.*'
jobs:
release:
  strategy:
    fail-fast: false
  runs-on: ubuntu-latest   
  steps:
    - uses: actions/checkout@v2
    - name: Setup dotnet 5.0
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 5.0.x
    - name: Clean
      run: dotnet clean -c Release
    - name: Build
      run: dotnet build -c Release
    - name: Test
      run: dotnet test -c Release -r nuget-package --no-build -l trx --verbosity=normal
    - name: Pack
      run: dotnet pack src/ForeverFactory/ForeverFactory.csproj -c Release --no-build -o nuget-package
    - name: Publish to Nuget.org
      run: dotnet nuget push nuget-package/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json # 1
  1. Quando publicamos os arquivos .nupkg, o CLI do NuGet identifica a presença dos pacotes de símbolos .snpkg e publica eles também

A primeira coisa que precisamos confirmar, é se os símbolos estão sendo publicados corretamente no feed do projeto no NuGet.org:

Página do projeto ForeverFactory aberta no site nuget.org, mostrando que os pacotes de binários e de símbolos foram publicados
Pacotes de binários símbolos publicados com sucesso

Por fim, podemos validar o pacote publicado usando o NGPE. Para isso, basta abrir o pacote a partir do feed usando a opção File -> Open from Feed...:

Pacote publicado aberto no NuGet Package Explorer, com todas as validações passando
Validações passando

#Conclusão

Seguindo estes passos, habilitamos o Source Link na nossa biblioteca, tornando-a muito mais fácil de depurar, e tornando mais fácil a vida dos usuários.

Gostou do artigo? Compartilhe nas redes sociais ou deixe um comentário abaixo!

Comentários