Partas.Build
0.1.0
See the version list below for details.
dotnet add package Partas.Build --version 0.1.0
NuGet\Install-Package Partas.Build -Version 0.1.0
<PackageReference Include="Partas.Build" Version="0.1.0" />
<PackageVersion Include="Partas.Build" Version="0.1.0" />
<PackageReference Include="Partas.Build" />
paket add Partas.Build --version 0.1.0
#r "nuget: Partas.Build, 0.1.0"
#:package Partas.Build@0.1.0
#addin nuget:?package=Partas.Build&version=0.1.0
#tool nuget:?package=Partas.Build&version=0.1.0
Partas.Build
Motivation
I hate CICD/CLI plumbing.
At the same time, it saves me from headache when I return to projects later.
System.CommandLine is great, comes with lots of batteries, and there exists
a great enough wrapper for it with FSharp.SystemCommandLine.
Fun.Build looks good for a github yaml type vibe of making workflows. But a majority
of it is hampered by the outdated command line parsing, and lack of typing.
So I combined Fun.Build with the strong typing of FSharp.SystemCommandLine builders,
and dog fed it to this repos own CI/CD plumbing.
So this begs the question: do I get friends now?
No. This still sounds useless
Rude.
Well, we've made everything compositional with a minimal contract at layers to bind command line arguments to their real values.
// ==== CLI OPTIONS/ARGS
// We don't need CEs EVERYWHERE.
// This is beautiful and readable.
let quick =
Input.option<bool> "--quick"
|> Input.alias "-q"
|> Input.desc "Skips installations, linting, and other checks"
let config =
Input.option<string> "--configuration"
|> Input.alias "-c"
|> Input.desc "Build/pack configuration"
|> Input.def "Release"
|> Input.arity Arity.ExactlyOne
|> Input.helpName "Debug|Release"
|> Input.acceptOnlyFromAmong [ "Debug"; "Release" ]
let clean (skip: bool) = stage "clean" {
when' (not skip)
run (fun _ ->
!! "**/**/bin" // Fake.IO.Globbing.Operators
++ "temp"
-- "bin"
|> Shell.cleanDirs
)
}
let restore (skip: bool) = stage "restore" {
when' (not skip)
run "dotnet tool restore --verbosity q"
run (cmd $"dotnet restore {Solutions.Main}")
}
let build (config: string) (skip: bool) =
stage "build" {
when' (not skip)
run (cmd $"dotnet build {Projects.FsProj.Solution} -c {config}")
}
module Commands =
let build = command "build" {
description "Builds the solution"
pipeline "build" {
workingDir root
// The binding of inputs are collected and
// deduped by the CEs, so you never have to
// directly add them to the command (but you still can)
let options = inputs {
let! skip = quick
and! config = config
return {| skip = skip; config = config |}
}
restore options.skip
clean options.skip
build options.config options.skip
}
}
This makes it easier to declare what options an operation depends on immediately when writing it though. So the alternative would be this:
let build = inputs {
let! config = config
and! quick = skip
return stage "build" {
when' (not skip)
run (cmd $"dotnet build {Projects.FsProj.Solution} -c {config}")
}
}
module Commands =
let build = command "build" {
description "Builds the solution"
pipeline "build" {
workingDir root
restore
clean
build
}
}
nice
Friends now?
Documentation
Not yet.
Soon. Maybe if we're friends?
Development
Build CLI
Every repository task runs through the Build project rather than a script, so
the tasks are typed, debuggable, and discoverable:
dotnet run --project Build.fsproj -- --help
| Command | What it does |
|---|---|
build |
Restores and builds the solution |
test |
Builds and runs the Expecto suite |
publish |
Packs and pushes to NuGet (--nuget-key; falls back to the local feed) |
docs |
Builds the fsdocs site (--watch to serve it) |
Flags belong to the commands whose stages read them: --quick skips restores
and the clean, --skip-tests skips the suite, --configuration picks the
configuration. None of them is registered by hand — see Adding a step.
Layout
Build.fsproj the build CLI
Build/
TargetOperators.fs list-taking FAKE target operators
Spec.fs typed repository paths and the CLI options
Program.fs the stages and the commands
src/Partas.Build/ the library
tests/Partas.Build.Tests/ the Expecto suite
Adding a project
Spec.fs addresses the repository through EasyBuild.FileSystemProvider, so
paths are checked when the build project compiles. After adding a project,
register it in Spec.fs:
module Projects =
module Directory =
type Solution = Root.src.``Partas.Build``
type NewThing = Root.src.``Partas.NewThing``
A typo, or a project renamed without updating the build, then fails at compile time rather than halfway through a release.
Adding a step
A step is a stage of a pipeline. A stage that needs a flag binds it in an
inputs { } block, which is also what makes the flag appear in --help:
let myStep = inputs {
let! quick = Options.quick
return stage "my step" {
when' (not quick)
run (cmd $"dotnet ... {Projects.FsProj.Solution}")
}
}
Add it to any command's pipeline { }. The condition stays in the stage, so the
command carries no flags of its own, and adding the stage to a second command
registers --quick there too.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FSharp.Core (>= 10.1.400)
- FsToolkit.ErrorHandling (>= 5.2.0)
- Spectre.Console (>= 0.57.2)
- System.CommandLine (>= 2.0.11)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Partas.Build:
| Package | Downloads |
|---|---|
|
Partas.Build.ExternalAnnotations
Package Description |
|
|
Partas.Build.Baked
Prebaked options, arguments, and composed stages for Partas.Build |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.5 | 36 | 9/21/2026 |
| 0.6.4 | 31 | 9/21/2026 |
| 0.6.3 | 40 | 9/19/2026 |
| 0.6.2 | 49 | 9/19/2026 |
| 0.6.1 | 39 | 9/19/2026 |
| 0.6.1-alpha.1 | 36 | 9/18/2026 |
| 0.5.0 | 67 | 9/16/2026 |
| 0.4.0-alpha.3 | 113 | 9/5/2026 |
| 0.4.0-alpha.2 | 47 | 9/4/2026 |
| 0.4.0-alpha.1 | 52 | 9/3/2026 |
| 0.3.0 | 147 | 8/29/2026 |
| 0.2.3 | 99 | 8/27/2026 |
| 0.2.2 | 88 | 8/27/2026 |
| 0.2.1 | 90 | 8/26/2026 |
| 0.2.0 | 98 | 8/25/2026 |
| 0.1.4 | 164 | 8/25/2026 |
| 0.1.3 | 104 | 8/23/2026 |
| 0.1.2 | 96 | 8/23/2026 |
| 0.1.1 | 97 | 8/22/2026 |
| 0.1.0 | 96 | 8/22/2026 |