Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ CleanArtifacts → { Version, Restore → Build → Test, Restore → Lint } →

`Lint` and `ReportEligibility` are gates and reports, not inputs: nothing depends on either.

`CleanArtifacts` deletes and recreates `Build:ArtifactsFolder` before anything else runs, so pack, validation, publishing, and release uploads only ever see the packages from the current run (set `Build:CleanArtifacts=false` to keep existing artifacts). `Version` resolves the release units from `Version:Source` (default: the `version` field of the root `package.json`); `Version:Strictness` defaults to `NuGet`, which accepts four-part versions. `ReportEligibility` evaluates the release rules and reports the verdict without acting on it. Lint restores local tools and runs CSharpier. Tests are discovered under `Build:TestRoot`/`Build:TestPatterns` and run with a TUnit tree-node filter (or an xUnit filter). Pack validation inspects each `.nupkg`/`.snupkg` against required/forbidden content rules (glob patterns) and can enforce source link, deterministic builds, and compiler flags on the packaged assemblies. Analyzer-only packages can embed portable PDBs under `analyzers/dotnet/` without requiring a `.snupkg`. Publication and GitHub release steps are controlled by `Release:Mode` (`None`, `LocalNuGet`, `NuGet`, `GitHubRelease`) — a preset over the independent `Release:Publish` and `Release:GitHubRelease` switches — and by the `Build__Run*` switches. `Release:DryRun` runs everything and skips both publish steps. `LocalNuGet` is only honoured when the tool runs locally; it is ignored in CI (for example via a reusable workflow).
`CleanArtifacts` deletes and recreates `Build:ArtifactsFolder` before anything else runs, so pack, validation, publishing, and release uploads only ever see the packages from the current run (set `Build:CleanArtifacts=false` to keep existing artifacts). `Version` resolves the release units from `Version:Source` (default: the `version` field of the root `package.json`); `Version:Strictness` defaults to `NuGet`, which accepts four-part versions. `ReportEligibility` evaluates the release rules and reports the verdict without acting on it. Lint restores local tools and runs CSharpier (`Build:CSharpierPath` runs a vendored CSharpier assembly instead, for an unreleased build). Tests are discovered under `Build:TestRoot`/`Build:TestPatterns` and run with a TUnit tree-node filter (or an xUnit filter). Pack validation inspects each `.nupkg`/`.snupkg` against required/forbidden content rules (glob patterns) and can enforce source link, deterministic builds, and compiler flags on the packaged assemblies. Analyzer-only packages can embed portable PDBs under `analyzers/dotnet/` without requiring a `.snupkg`. Publication and GitHub release steps are controlled by `Release:Mode` (`None`, `LocalNuGet`, `NuGet`, `GitHubRelease`) — a preset over the independent `Release:Publish` and `Release:GitHubRelease` switches — and by the `Build__Run*` switches. `Release:DryRun` runs everything and skips both publish steps. `LocalNuGet` is only honoured when the tool runs locally; it is ignored in CI (for example via a reusable workflow).

## Repository CI/CD

Expand Down
1 change: 1 addition & 0 deletions docs/wiki/Configuration-Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ Malformed JSON in a configuration file fails with the file path and, where the p
| `TestFramework` | `TUnit` | `TUnit` (tree-node filter) or `xUnit` (VSTest filter) |
| `TestFilter` | `/*/*/*/*/` | TUnit tree-node filter or xUnit `--filter`; empty disables it |
| `RunLint` | `true` | Restore local tools and run CSharpier check (dotnet) or `format:check` + `lint` scripts (Web) |
| `CSharpierPath` | unset | Path (relative to the repository root, or absolute) to a CSharpier CLI `.dll` to run with `dotnet` instead of the `dotnet-tools.json` manifest tool. For a repository that needs an unreleased CSharpier build, for example C# 15 union support (dotnet only) |
| `RunPack` | `true` | Enable packing |
| `ValidatePack` | `true` | Enable pack validation (dotnet only; always skipped for Web) |
| `WebInstallCommand` | `bun install` | Install command for `ProjectType=Web` |
Expand Down
2 changes: 1 addition & 1 deletion docs/wiki/Pipeline-Modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Depends on `RestoreModule`.

Depends on `RestoreModule` (Web lint invokes tooling installed by the restore step, so it must wait for `bun install`; dotnet lint is unaffected beyond running after restore). Skip condition: skipped when `Build:RunLint` is false.

- **DotNet**: restores the repository's local tools (`dotnet tool restore` against `.config/dotnet-tools.json`, retried up to 3 times with a 2-second backoff on failure) and then runs `dotnet tool run csharpier check <repository root>`. The repository root is resolved by walking up to the nearest `package.json`.
- **DotNet**: restores the repository's local tools (`dotnet tool restore` against `.config/dotnet-tools.json`, retried up to 3 times with a 2-second backoff on failure) and then runs `dotnet tool run csharpier check <repository root>`. The repository root is resolved by walking up to the nearest `package.json`. When `Build:CSharpierPath` is set, the manifest tool is bypassed: the module runs `dotnet <path> check <repository root>` against the vendored assembly instead and does not restore local tools.
- **Web**: runs `Build:WebFormatCheckCommand` (default `bun run format:check`) then `Build:WebLintCommand` (default `bun run lint`). Each step is skipped when the corresponding script is not declared in the root `package.json`.

## RunTestsModule
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "build",
"version": "0.5.4",
"version": "0.6.0",
"description": "Shared build/test/release pipeline for the purview-dev organisation, packaged as a pinned .NET tool with a GitHub composite action and reusable workflows.",
"license": "MIT",
"author": {
Expand Down
6 changes: 5 additions & 1 deletion purview-build.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
"type": "boolean",
"default": true
},
"CSharpierPath": {
"type": "string",
"description": "Path (relative to the repository root, or absolute) to a CSharpier CLI assembly (a .dll) to run with dotnet instead of the repository\u0027s dotnet-tools.json manifest tool. Use when a repository needs an unreleased CSharpier build (for example C# 15 union support); leave unset to run the manifest tool."
},
"RunPack": {
"type": "boolean",
"default": true
Expand Down Expand Up @@ -489,4 +493,4 @@
"patternProperties": {
"^\\$": true
}
}
}
1 change: 1 addition & 0 deletions skills/purview-build-configuration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ for an environment variable of that name before anything else.
| `ArtifactsFolder` | `artifacts` | package output directory |
| `CleanArtifacts` | `true` | delete and recreate `ArtifactsFolder` before anything is produced. Ignored when `RunPack` is `false` |
| `RunTests` / `RunLint` / `RunPack` / `ValidatePack` | `true` | the per-stage switches |
| `CSharpierPath` | unset | path (relative to the repository root, or absolute) to a CSharpier CLI `.dll` run with `dotnet` instead of the `dotnet-tools.json` manifest tool, for an unreleased CSharpier build (dotnet only) |
| `TestRoot` | `src/tests` | discovery root, relative to the repository root |
| `TestPatterns` | `*Tests.csproj` | comma-separated search patterns under `TestRoot` |
| `TestProjects` | `*` | comma-separated names/globs to actually run |
Expand Down
3 changes: 2 additions & 1 deletion skills/purview-build-pipeline/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ Depends on `Restore` — Web lint invokes tooling that `bun install` put there.

- **DotNet** — `dotnet tool restore` (retried up to 3 times with a 2-second backoff), then
`dotnet tool run csharpier check <repository root>`. The root is resolved by walking up to the
nearest `package.json`.
nearest `package.json`. When `Build:CSharpierPath` is set, the manifest tool is bypassed: the module
runs `dotnet <path> check <repository root>` against the vendored assembly and restores nothing.
- **Web** — `WebFormatCheckCommand` then `WebLintCommand`, each skipped when the corresponding
script is not declared in the root `package.json`.

Expand Down
31 changes: 31 additions & 0 deletions src/src/Build/Modules/LintModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ CancellationToken cancellationToken
if (settings.Value.ProjectType == ProjectType.Web)
return await RunWebLintAsync(context, repositoryRoot, cancellationToken);

// A repository that needs an unreleased CSharpier (for example one that formats C# 15 unions)
// points CSharpierPath at a CLI assembly it vendors itself. The manifest tool is then bypassed
// entirely, so no `dotnet tool restore` is required.
if (!string.IsNullOrWhiteSpace(settings.Value.CSharpierPath))
return await RunCSharpierAsync(
context,
repositoryRoot,
settings.Value.CSharpierPath,
cancellationToken
);

var dotnet = context.DotNet();

const int maxAttempts = 3;
Expand Down Expand Up @@ -65,6 +76,26 @@ Task<CommandResult> Restore() =>
);
}

async Task<CommandResult?> RunCSharpierAsync(
IModuleContext context,
string repositoryRoot,
string cSharpierPath,
CancellationToken cancellationToken
)
{
var path = Path.IsPathRooted(cSharpierPath)
? cSharpierPath
: Path.Combine(repositoryRoot, cSharpierPath);

context.Logger.LogInformation("Running CSharpier from {Path}.", path);

return await context.Shell.Command.ExecuteCommandLineTool(
DotNetCLIOptions.Create(path, "check", repositoryRoot),
new() { WorkingDirectory = repositoryRoot },
cancellationToken: cancellationToken
);
}

async Task<CommandResult?> RunWebLintAsync(
IModuleContext context,
string repositoryRoot,
Expand Down
8 changes: 8 additions & 0 deletions src/src/Build/Settings/BuildSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ public sealed class BuildSettings

public bool RunLint { get; init; } = true;

/// <summary>
/// Path (relative to the repository root, or absolute) to a CSharpier CLI assembly (a <c>.dll</c>)
/// to run with <c>dotnet</c> instead of the repository's <c>dotnet-tools.json</c> manifest tool.
/// Use when a repository needs an unreleased CSharpier build (for example C# 15 union support);
/// leave unset to run the manifest tool.
/// </summary>
public string? CSharpierPath { get; init; }

public bool RunPack { get; init; } = true;

public bool ValidatePack { get; init; } = true;
Expand Down
1 change: 1 addition & 0 deletions src/src/Build/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"TestFramework": "TUnit",
"TestFilter": "/*/*/*/*/",
"RunLint": true,
"CSharpierPath": null,
"RunPack": true,
"ValidatePack": true,
"WebInstallCommand": "bun install",
Expand Down
Loading