MSBuild has no built-in knowledge of any source control system or provider. In order to retrieve information from the source control the project needs to include a package reference to the appropriate source control package. Microsoft provides source control packages for Git and TFVC managers:
- Microsoft.Build.Tasks.Git (release)
- Microsoft.Build.Tasks.Tfvc (experimental)
These packages implement a protocol defined by MSBuild that allows extraction of necessary information from the source control system during build. This protocol can be implemented by third party packages in order to support other source control systems.
Adding one of these packages to the project enables MSBuild to automatically detect the current source revision id (Git commit hash, TFS shelveset number), the repository URL, source files that are not tracked by the source control and other useful information.
Having this information available enables the following features:
- Including source revision id in
AssemblyInformationalVersionAttribute
and in NuSpec of the package produced by the project. - Automatic detection and publishing of the repository URL.
- Embedding sources to the PDB that are not tracked by source control.
- Generating Source Link that enables debuggers to find sources when stepping through the DLL/EXE produced by the project.
To generate Source Link having just the source control package is not sufficient, since various source control providers (hosts) differ in the way how they expose the content of the hosted repositories. A package specific to the provider is needed. The following Source Link packages have been released by Microsoft:
- Microsoft.SourceLink.GitHub (depends on Microsoft.Build.Tasks.Git package)
- Microsoft.SourceLink.AzureRepos.Git (depends on Microsoft.Build.Tasks.Git package)
- Microsoft.SourceLink.AzureDevOpsServer.Git (depends on Microsoft.Build.Tasks.Git package)
- Microsoft.SourceLink.GitLab (depends on Microsoft.Build.Tasks.Git package)
- Microsoft.SourceLink.Bitbucket.Git (depends on Microsoft.Build.Tasks.Git package)
In addition an experimental package is available for TFVC server:
- Microsoft.SourceLink.AzureRepos.Tfvc (depends on experimental Microsoft.Build.Tasks.Tfvc package)
The system is extensible and custom packages that handle other source control providers can be developed and used. See Custom Source Link packages for details.
Each Source Link package depends on the corresponding source control package. Referencing a Source Link package makes the dependent source control package also referenced, thus providing the other source control features to the project.
Note that it is possible and supported to reference multiple Source Link packages in a single project provided they depend on the same source control package. This is necessary when the project sources are stored in mutliple submodules hosted by different providers (e.g. Azure Repos repository containing a GitHub submodule). See Configuring Projects with Multiple Source Link Providers for details.
To avoid inadvertently publishing private repository data, the URL and branch of the repository supplied by the CI server or retrieved from the source control manager
are not consumed directly. Instead, they are stored in the PrivateRepositoryUrl
and SourceBranchName
variables.
If PublishRepositoryUrl
is set to true
by the project, these properties are published into the RepositoryUrl
and RepositoryBranch
properties,
which are used e.g. in the nuspec file generated for the NuGet package produced by the project.
Set EmbedAllSources
to true
to instruct the build system to embed all project source files into the generated PDB.
Set EmbedUntrackedSources
to true
to instruct the build system to embed project source files that are not tracked by the source control or imported from a source package to the generated PDB.
Has no effect if EmbedAllSources
is true.
If the project generates additional source files that are added to Compile
item group in a custom target, this target must run before BeforeCompile
target (specify BeforeTargets="BeforeCompile"
).
Otherwise, these additional source files will not be automatically embedded into the PDB.
Set ContinuousIntegrationBuild
to true
to indicate that the build executes on a build/CI server.
ContinuousIntegrationBuild
variable is used within the build system to enable settings that only apply to official builds, as opposed to local builds on developer machine. An example of such setting is DeterministicSourcePaths.
By setting DeterministicSourcePaths
to true the project opts into mapping all source paths included in the project outputs to deterministic values, i.e. values that do not depend on the exact location of the project sources on disk, or the operating system on which the project is being built.
Only set DeterministicSourcePaths
to true on a build/CI server, never for local builds.
In order for the debugger to find source files when debugging a locally built binary, the PDB must contain original, unmapped local paths.
Starting with .NET Core SDK 2.1.300, a fully deterministic build is turned on when both Deterministic
and ContinuousIntegrationBuild
properties are set to true
.
The following project settings result in repository URL and commit hash automatically detected and included in NuSpec, commit hash included in AssemblyInformationalVersionAttribute
, all source files available on GitHub linked via Source Link (including those in submodules) and source files not available on GitHub embedded in the PDB.
Note that .NET Core SDK 2.1.300 is required.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
</ItemGroup>
</Project>
An additional configuration might be required when a project references multiple Source Link packages and the source control is hosted on a custom domain. By default a Source Link package infers the domain of the source control host from the origin
remote URL. For example, when a GitLab repository is cloned from http://git.contoso.com
and a project only references Microsoft.SourceLink.GitLab package the package infers that git.contoso.com
must be a hosting GitLab service. However, when multiple packages are used in the project it's not clear which service is git.contoso.com
domain hosting.
Each package defines an msbuild item group named SourceLink{provider}Host
, where {provider}
is the source control provider the package is built for (e.g. SourceLinkGitHubHost
, SourceLinkGitLabHost
, SourceLinkVstsGitHost
etc.). The item group allows the project to specify domain(s) that correspond to the source control provider of the package. For example, the following setting assigns git.contoso.com
domain to GitHub package:
<ItemGroup>
<SourceLinkGitHubHost Include="git.contoso.com"/>
</ItemGroup>
Content URL is the URL where the raw source files can be downloaded from. Items of SourceLink*Host
item group allow to specify the content URL if necessary. The content URL doesn't need to be specified in most cases as it is inferred from the domain.
The default content URLs for each package is listed below (GetSourceLinkUrlGitTask.GetDefaultContentUriFromHostUri API):
content URL | |
---|---|
GitLab | https://{domain} |
GitHub | https://{domain}/raw |
AzureRepos.Git | https://{domain} |
BitBucket | https://{domain} |
To override the above defaults specify ContentUrl
metadata on the item in build/{PackageName}.props
in the Source Link package. For example, GitHub.com server provides content on a CDN domain raw.githubusercontent.com
:
<ItemGroup>
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
</ItemGroup>
When true
and a source control package is present the SourceRevisionId
is included in the AssemblyInformationalVersionAttribute
.
The default value is true
. Set to false
to suppress publishing SourceRevisionId
to the attribute.
Set by target InitializeSourceControlInformationFromSourceControlManager
and consumed by NuGet Pack
target and GenerateAssemblyInfo
target.
May be used by custom targets that need this information.
Set by target InitializeSourceControlInformationFromSourceControlManager
and consumed by NuGet Pack
target.
May be used by custom targets that need this information.
This property is implicitly set to true
by a Source Link package. Including a Source Link package thus enables Source Link generation unless explicitly disabled by the project by setting this property to false
.
Item group that lists all source roots that the project source files reside under and their mapping to source control server URLs. This includes both source files under source control as well as source files in source packages.
Source root metadata:
- Identity: full path to the source root directory ending with a directory separator
All source control roots have the following metadata:
- SourceControl: the name of source control system, if the directory is a source source control root (e.g.
git
,tfvc
, etc.) - RevisionId: revision id (e.g. git commit hash)
Additional source-control specific metadata may be defined (depends on the source control system).
For example, for Git:
- RepositoryUrl: e.g.
http://github.com/dotnet/corefx
- BranchName: e.g.
refs/heads/main
(may be null if branch is in a detached HEAD state)
For TFVC:
- CollectionUrl
- ProjectId
- ServerPath
Nested source control roots have the following metadata (e.g. submodules):
- NestedRoot: URL to the source root relative to the containing source root (e.g.
src/submodules/mysubmodule
) - ContainingRoot: the identity if the containing source root
Source roots not under source control:
- SourceLinkUrl: URL to use in source link mapping, including
*
wildcard (e.g.https://raw.githubusercontent.com/dotnet/roslyn/42abf2e6642db97d2314c017eb179075d5042028/src/Dependencies/CodeAnalysis.Debugging/*
)
Each Source Link package is expected to provide mapping of repository URLs to corresponding content URLs that provide source file content.
The content URL shall identify an end-point that responds to HTTP GET request with content of the source file identified in the query. The end-point may require authentication.
The package shall depend on an appropriate source-control package (Microsoft.Build.Tasks.Git, Microsoft.Build.Tasks.Tfvc, etc.).
The package shall include build/{PackageName}.props
and build/{PackageName}.targets
files that get automatically included by NuGet into the project that references the package.
build/{PackageName}.props
file shall set EnableSourceLink
property to true
if it hasn't been set already, like so:
<PropertyGroup>
<EnableSourceLink Condition="'$(EnableSourceLink)' == ''">true</EnableSourceLink>
</PropertyGroup>
build/{PackageName}.targets
file shall add a uniquely named Source Link initialization target to SourceLinkUrlInitializerTargets
property, e.g. _InitializeXyzSourceLinkUrl
for source control provider called Xyz
.
<PropertyGroup>
<SourceLinkUrlInitializerTargets>$(SourceLinkUrlInitializerTargets);_InitializeXyzSourceLinkUrl</SourceLinkUrlInitializerTargets>
</PropertyGroup>
The initialization target shall update each item of the SourceRoot
item group that belongs to the Xyz provider with SourceLinkUrl
metadata that contains the final URL for this source root that will be stored in the Source Link file. It shall ignore any SourceRoot
items whose SourceControl
and RepositoryUrl
metadata it does not recognize.
See the implementation of GitHub Source Link package for an example.
In some scenarios it is desirable to build a repository from a directory that contains its sources but does not have .git
directory containing git metadata. Source Link does not require all git metadata to be present in the .git
directory, but
some metadata are needed. The following list describes the minimal set of directories and files that must be present in order
for Source Link to operate properly.
-
.git/HEAD
text file containing a commit SHA (e.g.
935f4b5c55167d9e4ed99b753f7340999d66d5de
) -
.git/config
configuration file that specifies
origin
remote URL (e.g.[remote "origin"] url="http://server.com/repo"
)
If the repository has submodules the file .gitmodules
must be present in the repository root and must list the
relative paths of all submodules:
[submodule "submodule-name"]
path = submodule-path
The .git/config
file must contain URLs of all initialized submodules:
[submodule "submodule-name"]
url = https://server.com/subrepo
The following additional files and directories must be present for each submodule:
-
.git/modules/submodule-name/HEAD
text file containing a commit SHA of the submodule
-
submodule-path/.git
text file pointing to the submodule metadata, with the following contents:
gitdir: ../.git/modules/submodule-name