Como adicionar Source Link num pacote NuGet

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>
É 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
Figura 1. 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
Figura 2. 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

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
Figura 3. 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.

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
Figura 4. 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
Figura 5. 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
Figura 6. 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.

Outras opções para compartilhar:
comments powered by Disqus