Skip to content

Latest commit

 

History

History
1503 lines (1129 loc) · 43.2 KB

README.md

File metadata and controls

1503 lines (1129 loc) · 43.2 KB

Build automation system for .NET

NuGet csi GitHub GitHub Build

C# interactive build automation system makes it easy to build .NET projects. It can be part of your solution as a regular console cross-platform .NET application, run C# scripts without compiling them, or even run in REPL mode - allowing you to run C# interactively.

Advantages

  • 3 compatible operating modes
  • Cross-platform
  • Debugging capability
  • No model limitations (Task, Target, DependsOn, etc.)
    • Regular .NET code
    • Best programming practices
  • Powerful API for building .NET projects
  • Summarised statistics

Operating modes

These modes are practically compatible, i.e., for example, a script can be run as a .NET project, and vice versa, with minimal or no changes.

Interactive (REPL)

Please see this page for installation details.

Launch the tool in the interactive mode:

dotnet csi

Simply enter C# commands sequentially one line after another and get the result in console output.

Running C# script

Run a specified script with a given argument:

dotnet csi ./MyDirectory/hello.csx World 

Run a single script located in the MyDirectory directory:

dotnet csi ./MyDirectory World
Usage details
dotnet csi [options] [--] [script] [script arguments]

Executes a script if specified, otherwise launches an interactive REPL (Read Eval Print Loop).

-- - Indicates that the remaining arguments should not be treated as options.

script - The path to the script file to run. If no such file is found, the command will treat it as a directory and look for a single script file inside that directory.

script arguments - Script arguments are accessible in a script via the global list Args[index] by an argument index.

@file - Read the response file for more options.

Supported options:

Option Description Alternative form
--help Show how to use the command. /?, -h, /h, /help
--version Display the tool version. /version
--source Specify the NuGet package source to use. Supported formats: URL, or a UNC directory path. -s, /s, /source
--property <key=value> Define a key-value pair(s) for the script properties called Props, which is accessible in scripts. -p, /property, /p
--property:<key=value> Define a key-value pair(s) in MSBuild style for the script properties called Props, which is accessible in scripts. -p:<key=value>, /property:<key=value>, /p:<key=value>, --property:key1=val1;key2=val2

.NET build project

Please see this page for details on how to install the project template.

Create a console project Build containing a script from the template build

dotnet new build -o ./Build

The created project contains 2 entry points:

  • Program.csx to run as a script
    dotnet csi ./Build
  • Program.cs to run as .NET application
    dotnet run --project ./Build

NuGet packages

Package name Link Description Installation
dotnet-csi NuGet Interactive .NET tool for REPL and running scripts dotnet tool install --global dotnet-csi
CSharpInteractive.Templates NuGet .NET build project template dotnet new install CSharpInteractive.Templates
CSharpInteractive NuGet A library for use in .NET build projects dotnet add package CSharpInteractive

Usage examples

API

Output, logging and tracing

Writing a line to a build log

WriteLine("Hello");

Writing a line highlighted with "Header" color to a build log

WriteLine("Hello", Header);

Writing an empty line to a build log

WriteLine();

Registering errors in the build log

Error("Error info", "Error identifier");

Registering warnings in the build log

Warning("Warning info");

Registering information in the build log

Info("Some info");

Registering trace information in the build log

Trace("Some trace info");

Arguments and parameters

Command line arguments

Args have got from the script arguments.

if (Args.Count > 0)
{
    WriteLine(Args[0]);
}

if (Args.Count > 1)
{
    WriteLine(Args[1]);
}

Using properties

WriteLine(Props["version"]);
WriteLine(Props.Get("configuration", "Release"));

// Some CI/CDs have integration of these properties.
// For example in TeamCity this property with all changes will be available in the next TeamCity steps.
Props["version"] = "1.1.6";

Microsoft DI

Using the Host property

Host is actually the provider of all global properties and methods.

var packages = Host.GetService<INuGet>();
Host.WriteLine("Hello");

Getting services

This method might be used to get access to different APIs like INuGet or ICommandLine.

GetService<INuGet>();

var serviceProvider = GetService<IServiceProvider>();
serviceProvider.GetService(typeof(INuGet));

Besides that, it is possible to get an instance of System.IServiceProvider to access APIs.

Using service collections

public void Run()
{
    var serviceProvider =
        GetService<IServiceCollection>()
            .AddTransient<MyTask>()
            .BuildServiceProvider();

    var myTask = serviceProvider.GetRequiredService<MyTask>();
    var exitCode = myTask.Run();
    exitCode.ShouldBe(0);
}

private class MyTask(ICommandLineRunner runner)
{
    public int? Run() => runner
        .Run(new CommandLine("whoami"))
        .EnsureSuccess()
        .ExitCode;
}

NuGet

Restoring a NuGet package of newest version

using HostApi;

IEnumerable<NuGetPackage> packages = GetService<INuGet>()
    .Restore(new NuGetRestoreSettings("IoC.Container").WithVersionRange(VersionRange.All));

Restoring a NuGet package by a version range for the specified .NET and path

using HostApi;

var packagesPath = Path.Combine(
    Path.GetTempPath(),
    Guid.NewGuid().ToString()[..4]);

var settings = new NuGetRestoreSettings("IoC.Container")
    .WithVersionRange(VersionRange.Parse("[1.3, 1.3.8)"))
    .WithTargetFrameworkMoniker("net5.0")
    .WithPackagesPath(packagesPath);

IEnumerable<NuGetPackage> packages = GetService<INuGet>().Restore(settings);

Command Line

Building custom command lines

using HostApi;

// Creates and run a simple command line 
"whoami".AsCommandLine().Run().EnsureSuccess();

// Creates and run a simple command line 
new CommandLine("whoami").Run().EnsureSuccess();

// Creates and run a command line with arguments 
new CommandLine("cmd", "/c", "echo", "Hello").Run();

// Same as previous statement
new CommandLine("cmd", "/c")
    .AddArgs("echo", "Hello")
    .Run().EnsureSuccess();

(new CommandLine("cmd") + "/c" + "echo" + "Hello")
    .Run().EnsureSuccess();

"cmd".AsCommandLine("/c", "echo", "Hello")
    .Run().EnsureSuccess();

("cmd".AsCommandLine() + "/c" + "echo" + "Hello")
    .Run().EnsureSuccess();

// Just builds a command line with multiple environment variables
var cmd = new CommandLine("cmd", "/c", "echo", "Hello")
    .AddVars(("Var1", "val1"), ("var2", "Val2"));

// Same as previous statement
cmd = new CommandLine("cmd") + "/c" + "echo" + "Hello" + ("Var1", "val1") + ("var2", "Val2");

// Builds a command line to run from a specific working directory 
cmd = new CommandLine("cmd", "/c", "echo", "Hello")
    .WithWorkingDirectory("MyDyrectory");

// Builds a command line and replaces all command line arguments
cmd = new CommandLine("cmd", "/c", "echo", "Hello")
    .WithArgs("/c", "echo", "Hello !!!");

Running a command line

using HostApi;

GetService<ICommandLineRunner>()
    .Run(new CommandLine("cmd", "/c", "DIR")).EnsureSuccess();

// or the same thing using the extension method
new CommandLine("cmd", "/c", "DIR")
    .Run().EnsureSuccess();

// using operator '+'
var cmd = new CommandLine("cmd") + "/c" + "DIR";
cmd.Run().EnsureSuccess();

// with environment variables
cmd = new CommandLine("cmd") + "/c" + "DIR" + ("MyEnvVar", "Some Value");
cmd.Run().EnsureSuccess();

Running a command line asynchronously

using HostApi;

await GetService<ICommandLineRunner>()
    .RunAsync(new CommandLine("cmd", "/C", "DIR")).EnsureSuccess();

// or the same thing using the extension method
var result = await new CommandLine("cmd", "/c", "DIR")
    .RunAsync().EnsureSuccess();

Running and analyzing an output

using HostApi;

var lines = new List<string>();
var result = new CommandLine("cmd", "/c", "SET")
    .AddVars(("MyEnv", "MyVal"))
    .Run(output => lines.Add(output.Line)).EnsureSuccess();

lines.ShouldContain("MyEnv=MyVal");

Running asynchronously in parallel

using HostApi;

var task = new CommandLine("cmd", "/c", "DIR")
    .RunAsync().EnsureSuccess();

var result = new CommandLine("cmd", "/c", "SET")
    .Run().EnsureSuccess();

await task;

Cancellation of asynchronous run

Cancellation will destroy the process and its child processes.

using HostApi;

var cancellationTokenSource = new CancellationTokenSource();
var task = new CommandLine("cmd", "/c", "TIMEOUT", "/T", "120")
    .RunAsync(default, cancellationTokenSource.Token);

cancellationTokenSource.CancelAfter(TimeSpan.FromMilliseconds(100));
task.IsCompleted.ShouldBeFalse();

Running with timeout

If timeout expired a process will be killed.

using HostApi;

var exitCode = new CommandLine("cmd", "/c", "TIMEOUT", "/T", "120")
    .Run(default, TimeSpan.FromMilliseconds(1))
    .ExitCode;

Docker CLI

Building a project in a docker container

using HostApi;

// Creates a base docker command line
var dockerRun = new DockerRun()
    .WithAutoRemove(true)
    .WithInteractive(true)
    .WithImage("mcr.microsoft.com/dotnet/sdk")
    .WithPlatform("linux")
    .WithContainerWorkingDirectory("/MyProjects")
    .AddVolumes((ToAbsoluteLinuxPath(Environment.CurrentDirectory), "/MyProjects"));

// Creates a new library project in a docker container
dockerRun
    .WithCommandLine(new DotNetCustom("new", "classlib", "-n", "MyLib", "--force"))
    .Run().EnsureSuccess();

// Builds the library project in a docker container
var result = dockerRun
    .WithCommandLine(new DotNetBuild().WithProject("MyLib/MyLib.csproj"))
    .Build().EnsureSuccess();

string ToAbsoluteLinuxPath(string path) =>
    "/" + path.Replace(":", "").Replace('\\', '/');

Running in docker

using HostApi;

// Creates some command line to run in a docker container
var cmd = new CommandLine("whoami");

// Runs the command line in a docker container
var result = new DockerRun(cmd, "mcr.microsoft.com/dotnet/sdk")
    .WithAutoRemove(true)
    .Run().EnsureSuccess();

.NET CLI

Adding a NuGet package

using HostApi;

var result = new DotNetAddPackage()
    .WithWorkingDirectory("MyLib")
    .WithPackage("Pure.DI")
    .Run().EnsureSuccess();

Adding a NuGet source

using HostApi;

new DotNetNuGetAddSource()
    .WithName("TestSource")
    .WithSource(source)
    .Run().EnsureSuccess();

Adding a project reference

using HostApi;

new DotNetAddReference()
    .WithProject(Path.Combine("MyTests", "MyTests.csproj"))
    .WithReferences(Path.Combine("MyLib", "MyLib.csproj"))
    .Run().EnsureSuccess();

new DotNetRemoveReference()
    .WithProject(Path.Combine("MyTests", "MyTests.csproj"))
    .WithReferences(Path.Combine("MyLib", "MyLib.csproj"))
    .Run().EnsureSuccess();

var lines = new List<string>();
new DotNetListReference()
    .WithProject(Path.Combine("MyTests", "MyTests.csproj"))
    .Run(output => lines.Add(output.Line));

lines.Any(i => i.Contains("MyLib.csproj")).ShouldBeFalse();

Adding projects to the solution file

using HostApi;

new DotNetNew()
    .WithTemplateName("sln")
    .WithName("NySolution")
    .WithForce(true)
    .Run().EnsureSuccess();

new DotNetSlnAdd()
    .WithSolution("NySolution.sln")
    .AddProjects(
        Path.Combine("MyLib", "MyLib.csproj"),
        Path.Combine("MyTests", "MyTests.csproj"))
    .Run().EnsureSuccess();

Adding project-to-project (P2P) references

using HostApi;

var result = new DotNetAddReference()
    .WithProject(Path.Combine("MyTests", "MyTests.csproj"))
    .WithReferences(Path.Combine("MyLib", "MyLib.csproj"))
    .Run().EnsureSuccess();

Building a project

using HostApi;

var messages = new List<BuildMessage>();
var result = new DotNetBuild()
    .WithWorkingDirectory("MyTests")
    .Build(message => messages.Add(message)).EnsureSuccess();

result.Errors.Any(message => message.State == BuildMessageState.StdError).ShouldBeFalse(result.ToString());
result.ExitCode.ShouldBe(0, result.ToString());

Building a project using MSBuild

using HostApi;

// Creates a new library project, running a command like: "dotnet new classlib -n MyLib --force"
new DotNetNew()
    .WithTemplateName("classlib")
    .WithName("MyLib")
    .WithForce(true)
    .Build().EnsureSuccess();

// Builds the library project, running a command like: "dotnet msbuild /t:Build -restore /p:configuration=Release -verbosity=detailed" from the directory "MyLib"
var result = new MSBuild()
    .WithWorkingDirectory("MyLib")
    .WithTarget("Build")
    .WithRestore(true)
    .AddProps(("configuration", "Release"))
    .WithVerbosity(DotNetVerbosity.Detailed)
    .Build().EnsureSuccess();

// The "result" variable provides details about a build
result.Errors.Any(message => message.State == BuildMessageState.StdError).ShouldBeFalse(result.ToString());
result.ExitCode.ShouldBe(0, result.ToString());

Cleaning a project

using HostApi;

// Clean the project, running a command like: "dotnet clean" from the directory "MyLib"
new DotNetClean()
    .WithWorkingDirectory("MyLib")
    .Build().EnsureSuccess();

Clearing the specified NuGet cache type

using HostApi;

new DotNetNuGetLocalsClear()
    .WithCacheLocation(NuGetCacheLocation.Temp)
    .Run().EnsureSuccess();

Creating a new project, configuration file, or solution based on the specified template

using HostApi;

new DotNetNew()
    .WithTemplateName("classlib")
    .WithName("MyLib")
    .WithForce(true)
    .Run().EnsureSuccess();

Deleting a NuGet package to the server

using HostApi;

new DotNetNuGetDelete()
    .WithPackage("MyLib")
    .WithPackageVersion("1.0.0")
    .WithSource(repoUrl)
    .Run().EnsureSuccess();

Disabling a NuGet source

using HostApi;

new DotNetNuGetDisableSource()
    .WithName("TestSource")
    .Run().EnsureSuccess();

Displaing template package metadata

using HostApi;

new DotNetNewDetails()
    .WithTemplateName("CSharpInteractive.Templates")
    .Run().EnsureSuccess();

Enabling a NuGet source

using HostApi;

new DotNetNuGetEnableSource()
    .WithName("TestSource")
    .Run().EnsureSuccess();

Enabling or disabling workload-set update mode

using HostApi;

new DotNetWorkloadConfig()
    .WithUpdateMode(DotNetWorkloadUpdateMode.WorkloadSet)
    .Run().EnsureSuccess();

Executing a dotnet application

using HostApi;
new DotNetExec()
    .WithPathToApplication(Path.Combine(path, "MyApp.dll"))
    .Run().EnsureSuccess();

Fixing (non code style) analyzer issues

using HostApi;

new DotNetFormatAnalyzers()
    .WithWorkingDirectory("MyLib")
    .WithProject("MyLib.csproj")
    .AddDiagnostics("CA1831", "CA1832")
    .WithSeverity(DotNetFormatSeverity.Warning)
    .Run().EnsureSuccess();

Fixing code style issues

using HostApi;

new DotNetFormatStyle()
    .WithWorkingDirectory("MyLib")
    .WithProject("MyLib.csproj")
    .AddDiagnostics("IDE0005", "IDE0006")
    .WithSeverity(DotNetFormatSeverity.Information)
    .Run().EnsureSuccess();

Formatting a code

using HostApi;

new DotNetFormat()
    .WithWorkingDirectory("MyLib")
    .WithProject("MyLib.csproj")
    .AddDiagnostics("IDE0005", "IDE0006")
    .AddIncludes(".", "./tests")
    .AddExcludes("./obj")
    .WithSeverity(DotNetFormatSeverity.Information)
    .Run().EnsureSuccess();

Getting a value of a specified NuGet configuration setting

using HostApi;

string? repositoryPath = default;
new DotNetNuGetConfigGet()
    .WithConfigKey("repositoryPath")
    .Run(output => repositoryPath = output.Line).EnsureSuccess();

Installing a template package

using HostApi;

new DotNetNewInstall()
    .WithPackage("Pure.DI.Templates")
    .Run().EnsureSuccess();

Installing optional workloads

using HostApi;

new DotNetWorkloadInstall()
    .AddWorkloads("aspire")
    .Run().EnsureSuccess();

Installing the .NET local tools that are in scope for the current directory

using HostApi;

// Creates a local tool manifest 
new DotNetNew()
    .WithTemplateName("tool-manifest")
    .Run().EnsureSuccess();

new DotNetToolRestore()
    .Run().EnsureSuccess();

Installing the specified .NET tool

using HostApi;

new DotNetToolInstall()
    .WithLocal(true)
    .WithPackage("dotnet-csi")
    .WithVersion("1.1.2")
    .Run().EnsureSuccess();

Installing workloads needed for a project or a solution

using HostApi;

new DotNetWorkloadRestore()
    .WithProject(Path.Combine("MyLib", "MyLib.csproj"))
    .Run().EnsureSuccess();

Invoking a local tool

using HostApi;

var script = Path.GetTempFileName();
File.WriteAllText(script, "Console.WriteLine($\"Hello, {Args[0]}!\");");

var stdOut = new List<string>();
new DotNetToolRun()
    .WithCommandName("dotnet-csi")
    .AddArgs(script)
    .AddArgs("World")
    .Run(output => stdOut.Add(output.Line))
    .EnsureSuccess();

// Checks stdOut
stdOut.Contains("Hello, World!").ShouldBeTrue();

Packing a code into a NuGet package

using HostApi;

// Creates a NuGet package of version 1.2.3 for the project
new DotNetPack()
    .WithWorkingDirectory("MyLib")
    .WithOutput(path)
    .AddProps(("version", "1.2.3"))
    .Build().EnsureSuccess();

Printing a dependency graph for NuGet package

using HostApi;

new DotNetNuGetWhy()
    .WithProject(Path.Combine("MyLib", "MyLib.csproj"))
    .WithPackage("MyLib.1.2.3.nupkg")
    .Run().EnsureSuccess();

Printing all .NET tools of the specified type currently installed

using HostApi;

new DotNetToolList()
    .WithLocal(true)
    .Run().EnsureSuccess();

new DotNetToolList()
    .WithGlobal(true)
    .Run().EnsureSuccess();

Printing all configured NuGet sources

using HostApi;

new DotNetNuGetListSource()
    .WithFormat(NuGetListFormat.Short)
    .Run().EnsureSuccess();

Printing all projects in a solution file

using HostApi;

var lines = new List<string>();
new DotNetSlnList()
    .WithSolution("NySolution.sln")
    .Run(output => lines.Add(output.Line))
    .EnsureSuccess();

Printing available templates to be run using dotnet new

using HostApi;

new DotNetNewList()
    .Run().EnsureSuccess();

Printing installed workloads

using HostApi;

new DotNetWorkloadList()
    .Run().EnsureSuccess();

Printing nuget configuration files currently being applied to a directory

using HostApi;

var configPaths = new List<string>();
new DotNetNuGetConfigPaths()
    .Run(output => configPaths.Add(output.Line)).EnsureSuccess();

Printing NuGet packages for a project

using HostApi;

new DotNetAddPackage()
    .WithWorkingDirectory("MyLib")
    .WithPackage("Pure.DI")
    .Run().EnsureSuccess();

var lines = new List<string>();
new DotNetListPackage()
    .WithProject(Path.Combine("MyLib", "MyLib.csproj"))
    .WithVerbosity(DotNetVerbosity.Minimal)
    .Run(output => lines.Add(output.Line));

lines.Any(i => i.Contains("Pure.DI")).ShouldBeTrue();

Printing project references for a project

using HostApi;

new DotNetAddReference()
    .WithProject(Path.Combine("MyTests", "MyTests.csproj"))
    .WithReferences(Path.Combine("MyLib", "MyLib.csproj"))
    .Run().EnsureSuccess();

var lines = new List<string>();
new DotNetListReference()
    .WithProject(Path.Combine("MyTests", "MyTests.csproj"))
    .Run(output => lines.Add(output.Line));

lines.Any(i => i.Contains("MyLib.csproj")).ShouldBeTrue();

Printing the latest available version of the .NET SDK and .NET Runtime, for each feature band

using HostApi;

var sdks = new List<Sdk>();
new DotNetSdkCheck()
    .Run(output =>
    {
        if (output.Line.StartsWith("Microsoft."))
        {
            var data = output.Line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            if (data.Length >= 2)
            {
                sdks.Add(new Sdk(data[0], NuGetVersion.Parse(data[1])));
            }
        }
    })
    .EnsureSuccess();

sdks.Count.ShouldBeGreaterThan(0);

record Sdk(string Name, NuGetVersion Version);

Printing the location of the specified NuGet cache type

using HostApi;

new DotNetNuGetLocalsList()
    .WithCacheLocation(NuGetCacheLocation.GlobalPackages)
    .Run().EnsureSuccess();

Publishing an application and its dependencies to a folder for deployment to a hosting system

using HostApi;

new DotNetPublish()
    .WithWorkingDirectory("MyLib")
    .WithFramework(framework)
    .WithOutput("bin")
    .Build().EnsureSuccess();

Pushing a NuGet package to the server

using HostApi;

new DotNetNuGetPush()
    .WithWorkingDirectory("MyLib")
    .WithPackage(Path.Combine("packages", "MyLib.1.0.0.nupkg"))
    .WithSource(repoUrl)
    .Run().EnsureSuccess();

Removing a NuGet package

using HostApi;

new DotNetAddPackage()
    .WithWorkingDirectory("MyLib")
    .WithPackage("Pure.DI")
    .Run().EnsureSuccess();

new DotNetRemovePackage()
    .WithWorkingDirectory("MyLib")
    .WithPackage("Pure.DI")
    .Run().EnsureSuccess();

Removing a project or multiple projects from the solution file

using HostApi;

new DotNetSlnRemove()
    .WithSolution("NySolution.sln")
    .AddProjects(
        Path.Combine("MyLib", "MyLib.csproj"))
    .Run().EnsureSuccess();

Removing an existing source from your NuGet configuration files

using HostApi;

new DotNetNuGetRemoveSource()
    .WithName("TestSource")
    .Run().EnsureSuccess(); 

Repairing workloads installations

using HostApi;

new DotNetWorkloadRepair()
    .Run().EnsureSuccess();

Restoring the dependencies and tools of a project

using HostApi;

new DotNetRestore()
    .WithProject(Path.Combine("MyLib", "MyLib.csproj"))
    .Build().EnsureSuccess();

Running a .NET application

// Adds the namespace "HostApi" to use .NET build API
using HostApi;

new DotNet()
    .WithPathToApplication(Path.Combine(path, "MyApp.dll"))
    .Run().EnsureSuccess();

Running a custom .NET command

using HostApi;

// Gets the dotnet version, running a command like: "dotnet --version"
NuGetVersion? version = default;
new DotNetCustom("--version")
    .Run(message => NuGetVersion.TryParse(message.Line, out version))
    .EnsureSuccess();

version.ShouldNotBeNull();

Running source code without any explicit compile or launch commands

using HostApi;

var stdOut = new List<string>();
new DotNetRun()
    .WithProject(Path.Combine("MyApp", "MyApp.csproj"))
    .Build(message => stdOut.Add(message.Text))
    .EnsureSuccess();

Running tests from the specified assemblies

using HostApi;

// Runs tests
var result = new VSTest()
    .AddTestFileNames(Path.Combine("bin", "MyTests.dll"))
    .WithWorkingDirectory(path)
    .Build().EnsureSuccess();

// The "result" variable provides details about build and tests
result.ExitCode.ShouldBe(0, result.ToString());
result.Summary.Tests.ShouldBe(1, result.ToString());
result.Tests.Count(test => test.State == TestState.Finished).ShouldBe(1, result.ToString());

Running tests under dotCover

using HostApi;

new DotNetToolInstall()
    .WithLocal(true)
    .WithPackage("JetBrains.dotCover.GlobalTool")
    .Run().EnsureSuccess();

// Creates a test command
var test = new DotNetTest()
    .WithProject("MyTests");

var dotCoverSnapshot = Path.Combine("MyTests", "dotCover.dcvr");
var dotCoverReport = Path.Combine("MyTests", "dotCover.html");
// Modifies the test command by putting "dotCover" in front of all arguments
// to have something like "dotnet dotcover test ..."
// and adding few specific arguments to the end
var testUnderDotCover = test.Customize(cmd =>
    cmd.ClearArgs()
    + "dotcover"
    + cmd.Args
    + $"--dcOutput={dotCoverSnapshot}"
    + "--dcFilters=+:module=TeamCity.CSharpInteractive.HostApi;+:module=dotnet-csi"
    + "--dcAttributeFilters=System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage");

// Runs tests under dotCover
var result = testUnderDotCover
    .Build().EnsureSuccess();

// The "result" variable provides details about a build
result.ExitCode.ShouldBe(0, result.ToString());
result.Tests.Count(i => i.State == TestState.Finished).ShouldBe(1, result.ToString());

// Generates a HTML code coverage report.
new DotNetCustom("dotCover", "report", $"--source={dotCoverSnapshot}", $"--output={dotCoverReport}", "--reportType=HTML")
    .Run().EnsureSuccess();

Searching all .NET tools that are published to NuGet

using HostApi;

new DotNetToolSearch()
    .WithPackage("dotnet-csi")
    .WithDetail(true)
    .Run().EnsureSuccess();

Searching for a NuGet package

using System.Text;
using System.Text.Json;
using HostApi;

var packagesJson = new StringBuilder();
new DotNetPackageSearch()
    .WithSearchTerm("Pure.DI")
    .WithFormat(DotNetPackageSearchResultFormat.Json)
    .Run(output => packagesJson.AppendLine(output.Line)).EnsureSuccess();

var result = JsonSerializer.Deserialize<Result>(
    packagesJson.ToString(),
    new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

result.ShouldNotBeNull();
result.SearchResult.SelectMany(i => i.Packages).Count(i => i.Id == "Pure.DI").ShouldBe(1);

record Result(int Version, IReadOnlyCollection<Source> SearchResult);

record Source(string SourceName, IReadOnlyCollection<Package> Packages);

record Package(
    string Id,
    string LatestVersion,
    int TotalDownloads,
    string Owners);

Searching for optional workloads

using HostApi;

new DotNetWorkloadSearch()
    .WithSearchString("maui")
    .Run().EnsureSuccess();

Searching for the templates

using HostApi;

new DotNetNewSearch()
    .WithTemplateName("build")
    .Run().EnsureSuccess();

Setting the value of a specified NuGet configuration setting

using HostApi;

new DotNetNuGetConfigSet()
    .WithConfigFile(configFile)
    .WithConfigKey("repositoryPath")
    .WithConfigValue("MyValue")
    .Run().EnsureSuccess();

Signing with certificate

using HostApi;

new DotNetNuGetSign()
    .AddPackages("MyLib.1.2.3.nupkg")
    .WithCertificatePath("certificate.pfx")
    .WithCertificatePassword("Abc")
    .WithTimestampingServer("http://timestamp.digicert.com/")
    .Run().EnsureSuccess();

Storing the specified assemblies in the runtime package store.

using HostApi;

new DotNetStore()
    .AddManifests(Path.Combine("MyLib", "MyLib.csproj"))
    .WithFramework("net8.0")
    .WithRuntime("win-x64")
    .Build();

Testing a project using the MSBuild VSTest target

using HostApi;

// Runs tests via a command
var result = new MSBuild()
    .WithTarget("VSTest")
    .WithWorkingDirectory("MyTests")
    .Build().EnsureSuccess();

// The "result" variable provides details about a build
result.ExitCode.ShouldBe(0, result.ToString());
result.Summary.Tests.ShouldBe(1, result.ToString());
result.Tests.Count(test => test.State == TestState.Finished).ShouldBe(1, result.ToString());

Testing from the specified project

using HostApi;

// Runs tests
var result = new DotNetTest()
    .WithWorkingDirectory("MyTests")
    .Build().EnsureSuccess();

Uninstalling a specified workload

using HostApi;

new DotNetWorkloadUninstall()
    .AddWorkloads("aspire")
    .Run().EnsureSuccess();

Uninstalling a template package

using HostApi;

new DotNetNewUninstall()
    .WithPackage("Pure.DI.Templates")
    .Run();

Uninstalling the specified .NET tool

using HostApi;

new DotNetToolUninstall()
    .WithPackage("dotnet-csi")
    .Run().EnsureSuccess();

Unsetting the value of a specified NuGet configuration setting

using HostApi;

new DotNetNuGetConfigUnset()
    .WithConfigKey("repositoryPath")
    .Run().EnsureSuccess();

Updating a NuGet source

using HostApi;

new DotNetNuGetUpdateSource()
    .WithName("TestSource")
    .WithSource(newSource)
    .Run().EnsureSuccess();

Updating installed template packages

using HostApi;

new DotNetNewUpdate()
    .Run().EnsureSuccess();

Updating installed workloads

using HostApi;

new DotNetWorkloadUpdate()
    .Run().EnsureSuccess();

Updating the specified .NET tool

using HostApi;

new DotNetToolUpdate()
    .WithLocal(true)
    .WithPackage("dotnet-csi")
    .WithVersion("1.1.2")
    .Run().EnsureSuccess();

Working with development certificates

using HostApi;

// Create a certificate, trust it, and export it to a PEM file.
new DotNetDevCertsHttps()
    .WithExportPath("certificate.pem")
    .WithTrust(true)
    .WithFormat(DotNetCertificateFormat.Pem)
    .WithPassword("Abc")
    .Run().EnsureSuccess();

Running C# script

using HostApi;

var script = Path.GetTempFileName();
File.WriteAllText(script, "Console.WriteLine($\"Hello, {Args[0]}!\");");

var stdOut = new List<string>();
new DotNetCsi()
    .WithScript(script)
    .AddArgs("World")
    .Run(output => stdOut.Add(output.Line))
    .EnsureSuccess();

// Checks stdOut
stdOut.Contains("Hello, World!").ShouldBeTrue();

Shutting down build servers

using HostApi;

// Shuts down all build servers that are started from dotnet.
new DotNetBuildServerShutdown()
    .Run().EnsureSuccess();

TeamCity API

TeamCity integration via service messages

For more details how to use TeamCity service message API please see this page. Instead of creating a root message writer like in the following example:

using JetBrains.TeamCity.ServiceMessages.Write.Special;
using var writer = new TeamCityServiceMessages().CreateWriter(Console.WriteLine);

use this statement:

using JetBrains.TeamCity.ServiceMessages.Write.Special;
using var writer = GetService<ITeamCityWriter>();

This sample opens a block My Tests and reports about two tests:

// Adds a namespace to use ITeamCityWriter
using JetBrains.TeamCity.ServiceMessages.Write.Special;

using var writer = GetService<ITeamCityWriter>();
using (var tests = writer.OpenBlock("My Tests"))
{
    using (var test = tests.OpenTest("Test1"))
    {
        test.WriteStdOutput("Hello");
        test.WriteImage("TestsResults/Test1Screenshot.jpg", "Screenshot");
        test.WriteDuration(TimeSpan.FromMilliseconds(10));
    }

    using (var test = tests.OpenTest("Test2"))
    {
        test.WriteIgnored("Some reason");
    }
}

For more information on TeamCity Service Messages, see this page.