Skip to content
nulleanPublic

About

Typed build targets for .NET: targets are union or DU cases, dependencies run first, and every target gets real --help. Built on argh.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

runna

Typed build targets for .NET: your targets are the cases of a C# union or an F# DU, dependencies run first, and every target gets real --help.

Runna on NuGet Runna.Fs on NuGet CI Docs

$ ./build.cs release --graph
release  Verify gates, pack, generate notes, diff APIs
├─ requires
│  ├─ pristine-check
│  └─ test
└─ composes
   ├─ pkg generate
   ├─ pkg validate
   ├─ release-notes
   └─ api-changes

Runs: pristine-check → test → pkg generate → pkg validate → release-notes → api-changes

Install

dotnet add package Runna      # C#
dotnet add package Runna.Fs   # F#

Quick start

The same three targets in either language: test needs build, which needs clean. It requires .NET 11.

C#

A file-based app next to your solution is enough.

#!/usr/bin/env -S dotnet --
#:package Runna@0.2.0
#:property GenerateDocumentationFile=true
#:property NoWarn=CS1591
using Nullean.Runna;
using static Nullean.Runna.Shell;

var app = new RunnaApp<Target>("build");

app.Bind(t => t.Value switch
{
    Clean => app.Target(() => Exec("dotnet clean")),
    Build => app.Target([new Clean()], () => Exec("dotnet build")),
    Test  => app.Target([new Build()], () => Exec("dotnet test")),
    _ => throw new InvalidOperationException($"unmapped target {t.Value}")
});

return await app.RunAsync(args);

/// <summary>Delete build output</summary>
record Clean;
/// <summary>Build the solution</summary>
record Build;
/// <summary>Build, then run the tests</summary>
record Test;

union Target(Clean, Build, Test);
./build.cs --help
./build.cs test --graph

F#

Targets are the cases of a DU, so a case you forget in Bind is a compiler warning (FS0025), and each target body is a Proc.Fs shell { } block. Put it in a small project, build/build.fsproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net11.0</TargetFramework>
    <!-- the /// comments become each target's description in the help -->
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Program.fs" />
    <PackageReference Include="Runna.Fs" Version="0.2.0" />
  </ItemGroup>
</Project>
open Nullean.Runna.Fs
open Proc.Fs

type Target =
    /// Delete build output
    | Clean
    /// Build the solution
    | Build
    /// Build, then run the tests
    | Test

[<EntryPoint>]
let main argv =
    let app = RunnaApp<Target>("build", None)

    app.Bind <| function
        | Clean -> target [] { sh $"dotnet clean" }
        | Build -> target [Clean] { sh $"dotnet build" }
        | Test  -> target [Build] { sh $"dotnet test" }

    app.RunAsync(argv).GetAwaiter().GetResult()
dotnet run --project build -- --help
dotnet run --project build -- test --graph

Both print the same help and plan. The package READMEs go further: typed per-target flags, namespaces, pipeline commands, parallel stages and the Runna / Runna.Fs specifics.

Packages

Package What it is
Runna C# build targets as the cases of a union NuGet
Runna.Fs F# build targets as DU cases NuGet

Learn more

MIT licensed. Copyright Nullean and contributors.

About

Typed build targets for .NET: targets are union or DU cases, dependencies run first, and every target gets real --help. Built on argh.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages