Saltar al contenido principal

Configurar Source Link en proyectos .NET y cómo utilizarlo en Visual Studio

Como miembro encargado de mantener algunos proyectos de código abierto en Xabaril y que mucha gente utiliza, creo que es crucial ayudar a los desarrolladores a que puedan depurar el código fuente de nuestras librerías con el fin de facilitarles la vida a la hora de solucionar problemas y poder entender mejor su funcionamiento. Por este motivo, en Xabaril hemos decidido habilitar el soporte Source Link en nuestros proyectos.

Antes de continuar, quiero agradecer a Unai Zorrilla por mostrarme las bondades de Source Link y ayudarme a configurarlo en Balea.

¿Qué es Source Link?

Source Link es una tecnología que permite a los desarrolladores depurar el código fuente de los paquetes NuGet, de modo que herramientas como Visual Studio pueden aprovecharse de esta característica para poder depurar su código fuente como si se tratase de un proyecto local más con el fin de poder solucionar problemas cuando se utilizan proyectos de código abierto.

Source Link en proyectos .NET Core

No sé si estás familiarizado con el archivo dependencies.props en proyectos .NET Core. Si no lo estás, te recomiendo su uso a partir de ahora en adelante.

dependencies.props sirve para evitar problemas como la consolidación de versiones de los paquetes NuGet en nuestras soluciones, ya que estamos definiendo las versiones en un solo lugar y cada proyecto define qué dependencia necesita:

<PropertyGroup Label="Package Dependencies">
<AspNetCoreVersion>3.1.3</AspNetCoreVersion>
<EntityFrameworkVersion>3.1.3</EntityFrameworkVersion>
</PropertyGroup>

En tu csproj tienes que definir que dependencias vas a usar:

 <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="$(AspNetCoreVersion)" />
  </ItemGroup>

De este modo si el equipo de ASP.NET CORE lanza una nueva versión ya no es necesario pasar por todos los proyectos uno por uno actualizando la versión, sólo tendrás que actualizar la versión en tu dependencies.props y aquellos proyectos que lo referencien se actualizarán automáticamente.

Otro archivo con el que deberías estar familiarizado es Directory.Build.props que facilita la compartición de metadatos comunes en toda la solución.

<Project>
  <Import Project="build/dependencies.props" />
  <PropertyGroup Label="Package information">
    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
    <PackageProjectUrl>http://github.com/xabaril/Balea</PackageProjectUrl>
    <RepositoryUrl>https://github.com/xabaril/Balea</RepositoryUrl>
    <Authors>Xabaril Contributors</Authors>
    <Company>Xabaril</Company>
  </PropertyGroup>
</Project>

En la primera línea, estamos importando el archivo dependencies.props y agregando algunos metadatos comunes para compartir en todos los proyectos como la información de la licencia, la url del proyecto, etc.

Después de una breve introducción a estos archivos, vamos a habilitar Source Link en nuestro proyecto. Para ello, necesitarás añadir estas líneas a tu Directory.Build.props

<!-- Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->
<PublishRepositoryUrl>true</PublishRepositoryUrl>

<!-- Embed source files that are not tracked by the source control manager in the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources>

<!-- Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>

Para los proyectos alojados en GitHub necesitarás referirte al paquete Source Link:

<ItemGroup>
  <SourceRoot Include="$(MSBuildThisFileDirectory)/"/>
  <PackageReference Include="Microsoft.SourceLink.GitHub" Version="$(MicrosoftSourceLinkGithub)">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
  </PackageReference>
</ItemGroup>

A continuación, tendrás que habilitar las compilaciones determinísticas (determinstic builds). En este repo Claire Novotny explica cómo funciona y por qué es tan importante.

«Las Deterministic Build son importantes ya que permiten verificar que el binario resultante se construyó a partir de la fuente especificada que ofrece trazabilidad»

Necesitarás añadir esto a tu Directory.Build.props si estás usando GitHub:

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

Hay un issue bien conocido con el atributo «EmbedUntrackedSources» que podría solucionarse con un pequeño parche temporal que es necesario para los SDKs antes del 3.1.300 (En este momento escribo este post 3.1.201).

Añade un nuevo archivo llamado Directory.Build.targets a nivel de solución con el contenido que se muestra a continuación:

<Project>

<PropertyGroup>
    <TargetFrameworkMonikerAssemblyAttributesPath>$([System.IO.Path]::Combine('$(IntermediateOutputPath)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))</TargetFrameworkMonikerAssemblyAttributesPath>
  </PropertyGroup>
  <ItemGroup>
    <!-- https://github.com/dotnet/sourcelink/issues/572 -->
    <EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
  </ItemGroup>
</Project>

Si quieres probar tus paquetes en una máquina local antes de publicarlos en el feed de NuGet, elimina la instrucción condicional de Msbuild.

<PropertyGroup>
    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

Ejecuta el siguiente comando para empaquetar tu proyecto…

dotnet pack <yoor_project>.csproj -c Release -o .\artifacts --include-symbols

SourceLink

Ahora abre NuGet Package Explorer y selecciona “Open a local package”

SourceLink

Localiza tu paquete NuGet…

SourceLink

… y deberías ver Source Link y la compilación determinista ya verificadas.

SourceLink

Ahora es momento de añadir de nuevo el condicional para las acciones de GitHub que has eliminado anteriormente para probarlo en tu máquina local y subir los cambios a tu repositorio. Necesitarás tener una GitHub Action para publicar tus paquetes en el feed NuGet.

Una vez que el paquete se haya publicado, abre de nuevo el NuGet Package Explorer, pero esta vez selecciona “Open a package from online feed”

SourceLink

Busca tu paquete de NuGet…

SourceLink

… y si has seguido correctamente los pasos, tu paquete debería ser válido y reflejar el último cambio de tu repo.

SourceLink

Configurar Visual Studio 2019 para Source Link

Por defecto, el servidor de símbolos NuGet está desactivado. Visual Studio 2019 necesita poder descargar estas fuentes para funcionar correctamente con Source Link.

Sigue esta ruta para activarlo: Tools -> Options -> Debugging -> Symbols.

SourceLink

Ahora, ves a Tools -> Options -> Debugging -> General y desmarca la casilla “Enable Just My Code”

SourceLink

Por defecto Source Link está habilitado en Visual Studio 2019, pero no tiene soporte para el Source Server:

SourceLink

Fallback to Git Credential Manager… permite a las herramientas que usan Git Credential Manager usar Source Link en repositorios privados.

Third party code

En mi caso, he creado una simple aplicación WebApi y he añadido los paquetes de Balea:

SourceLink

Ejecuta la aplicación web y haz clic en F11 para entrar:

SourceLink

Una ventana flotante aparecerá por primera vez donde nos informa que Source Link va a descargar el código de internet si no lo ha descargado previamente.

SourceLink

Una vez que el código fuente es descargado por Source Link, ya podemos depurarlo:

SourceLink

Y eso es todo! En este post he intentado mostraros cómo configurar Source Link en proyectos .NET y cómo aprovechar esta característica desde el Visual Studio. Si quieres ver un ejemplo completo, te recomiendo que le eches un vistazo a este repo.

luis ruiz pavon
Autor
Luis Ruiz Pavón
C3PO at Plain Concepts