The command-line surface RunOrBuildAsync dispatches on — one positional verb (build) followed by an optional base URL and output directory, or equivalent --base-url / --output named flags. Parsing lives in OutputOptions.FromArgs; any invocation whose first argument is not build falls through to app.RunAsync() with default OutputOptions.
Commands
| Command |
Arguments |
Effect |
| (none) |
— |
Dev-serve: app.RunAsync(). OutputOptions.FromArgs returns defaults (BaseUrl = "/", OutputDirectory = "output") but the crawler never runs. |
build |
[baseUrl] [outputDirectory] positional, or --base-url / --output named flags |
Static build: app.StartAsync(), resolve OutputGenerationService, HTTP-crawl the running host, write each response to OutputOptions.OutputDirectory, print BuildReport, set Environment.ExitCode = 1 on errors, app.StopAsync(). |
| anything else |
— |
Dev-serve fallback. Non-build args[0] is treated as unknown; positional args are not interpreted as a base URL or output directory (guards against dotnet test / dotnet watch emitting stray positional args). |
Positional arguments
| Position |
Name |
Default |
Description |
args[1] |
baseUrl |
/ |
The URL sub-path the site will be served from; materialized as OutputOptions.BaseUrl (a UrlPath). Promoted to args[2]'s slot if --base-url was already supplied. |
args[2] |
outputDirectory |
output |
The filesystem directory to write the generated site into; materialized as OutputOptions.OutputDirectory (a FilePath). Promoted if --output was already supplied. |
Named flags
| Flag |
Value form |
Maps to |
Notes |
--base-url |
--base-url /sub or --base-url=/sub |
OutputOptions.BaseUrl |
Read by TryReadFlag in OutputOptions.FromArgs. Case-insensitive match. |
--output |
--output dist or --output=dist |
OutputOptions.OutputDirectory |
Read by TryReadFlag. Case-insensitive match. |
Environment variables
| Variable |
Consumer |
Effect when set |
ASPNETCORE_URLS |
ASP.NET Core host |
Standard ASP.NET binding. RunOrBuildAsync resolves app.Urls.First() after StartAsync, falling back to http://localhost:5000 only when app.Urls is empty, so overriding this variable moves the crawler target. |
ASPNETCORE_ENVIRONMENT |
ASP.NET Core host |
Standard environment selection; Pennington's dev tooling (live reload, diagnostic overlay) gates on the build command-line argument, not on this variable. |
Listening port
Pennington uses the standard ASP.NET Core host, so the usual port-binding mechanisms apply.
| Mechanism |
Form |
Notes |
| CLI flag |
dotnet run -- --urls http://localhost:5101 |
Pass-through to the ASP.NET Core host argument parser. |
| Environment variable |
ASPNETCORE_URLS=http://localhost:5101 |
Same variable as the entry above. |
launchSettings.json |
profiles.<name>.applicationUrl |
Standard Properties/launchSettings.json profile entry. |
Pennington does not override any of these — the library adds middleware and endpoints on top of whatever URL Kestrel is told to listen on.
Exit codes
0 — build completed without errors (BuildReport.HasErrors == false), or dev-serve exited cleanly.
1 — build completed but the BuildReport contains at least one error diagnostic or failed page (BuildReport.HasErrors == true). Set explicitly by RunOrBuildAsync after writing the report.
Example
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPennington(penn =>
{
penn.SiteTitle = "My First Pennington Site";
penn.ContentRootPath = "Content";
penn.AddMarkdownContent<DocFrontMatter>(md =>
{
md.ContentPath = "Content";
md.BasePageUrl = "/";
});
});
var app = builder.Build();
app.UsePennington();
await app.RunOrBuildAsync(args);
dotnet run against this host serves live; dotnet run -- build generates to ./output/ at base URL /; dotnet run -- build /sub dist or dotnet run -- build --base-url=/sub --output=dist generates to ./dist/ at base URL /sub.
See also