diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index d89252c..5262a2a 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -8,44 +8,17 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
-env:
- DOTNET_NOLOGO: true
-
jobs:
build:
name: Build, lint & package
- runs-on: ubuntu-latest
- timeout-minutes: 30
- steps:
- - uses: actions/checkout@v7
- with:
- fetch-depth: 0
- fetch-tags: true
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v6
- with:
- dotnet-version: "10.0.x"
-
- - name: Cache NuGet packages
- uses: actions/cache@v4
- with:
- path: |
- ~/.nuget/packages
- ~/.local/share/NuGet
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj', '**/*.props', '**/*.targets', 'global.json') }}
- restore-keys: |
- ${{ runner.os }}-nuget-
-
- - name: Run PR pipeline (without tests)
- env:
- Build__RunTests: "false"
- run: dotnet run --project build/PipelineCLI/PipelineCLI.csproj --configuration Release
+ uses: purview-dev/build/.github/workflows/purview-build.yml@main
+ with:
+ build-version: "0.2.1"
+ run-tests: false
+ secrets: inherit
tests:
name: Tests (${{ matrix.project }})
- runs-on: ubuntu-latest
- timeout-minutes: 30
strategy:
fail-fast: false
matrix:
@@ -53,31 +26,11 @@ jobs:
- Analyzers.UnitTests.csproj
- Analyzers.IntegrationTests.csproj
- DotNetProjectSdk.IntegrationTests.csproj
- steps:
- - uses: actions/checkout@v7
- with:
- fetch-depth: 0
- fetch-tags: true
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v6
- with:
- dotnet-version: "10.0.x"
-
- - name: Cache NuGet packages
- uses: actions/cache@v4
- with:
- path: |
- ~/.nuget/packages
- ~/.local/share/NuGet
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj', '**/*.props', '**/*.targets', 'global.json') }}
- restore-keys: |
- ${{ runner.os }}-nuget-
-
- - name: Run tests for ${{ matrix.project }}
- env:
- Build__RunTests: "true"
- Build__TestProjects: ${{ matrix.project }}
- Build__RunLint: "false"
- Build__RunPack: "false"
- run: dotnet run --project build/PipelineCLI/PipelineCLI.csproj --configuration Release
+ uses: purview-dev/build/.github/workflows/purview-build.yml@main
+ with:
+ build-version: "0.2.1"
+ run-lint: false
+ run-pack: false
+ validate-pack: false
+ test-projects: ${{ matrix.project }}
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9825177..97abb5f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,51 +11,9 @@ concurrency:
jobs:
release:
name: Release packages
- runs-on: ubuntu-latest
- timeout-minutes: 30
- permissions:
- contents: write
- steps:
- - uses: actions/checkout@v7
- with:
- fetch-depth: 0
- fetch-tags: true
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v6
- with:
- dotnet-version: "10.0.x"
-
- - name: Cache NuGet packages
- uses: actions/cache@v4
- with:
- path: |
- ~/.nuget/packages
- ~/.local/share/NuGet
- key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props', '**/*.csproj', '**/*.props', '**/*.targets', 'global.json') }}
- restore-keys: |
- ${{ runner.os }}-nuget-
-
- - name: Check for version bump
- id: version
- shell: bash
- run: |
- VERSION=$(node -p "require('./package.json').version")
- TAG="v$VERSION"
- if git rev-parse "$TAG" >/dev/null 2>&1; then
- echo "Version $VERSION is already tagged as $TAG. Skipping release."
- echo "should_publish=false" >> "$GITHUB_OUTPUT"
- else
- echo "New version $VERSION detected. Releasing $TAG."
- echo "should_publish=true" >> "$GITHUB_OUTPUT"
- echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- fi
-
- - name: Run release pipeline
- if: steps.version.outputs.should_publish == 'true'
- env:
- Release__Mode: NuGet
- GITHUB_TOKEN: ${{ github.token }}
- NuGet__ApiKey: ${{ secrets.NUGET__APIKEY }}
- run: dotnet run --project build/PipelineCLI/PipelineCLI.csproj --configuration Release
+ uses: purview-dev/build/.github/workflows/purview-release.yml@main
+ with:
+ build-version: "0.2.1"
+ release-mode: NuGet
+ release-branch: main
+ secrets: inherit
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 76ebe1f..3f1a8f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -661,3 +661,5 @@ sketch
!build/
+
+.tools/
diff --git a/Justfile b/Justfile
index c5d8cce..06a71b7 100644
--- a/Justfile
+++ b/Justfile
@@ -4,8 +4,9 @@ solution_file := "src/DotNetProjectSdk.slnx"
build_configuration := "Release"
artifacts_folder := "./artifacts"
-pipeline_solution := "build/Pipeline.slnx"
-pipeline_project := "build/PipelineCLI/PipelineCLI.csproj"
+pipeline_version := "0.2.1"
+pipeline_feed := "https://api.nuget.org/v3/index.json"
+pipeline_tool := ".tools/purview-build/purview-build"
current_version := `node -p "require('./package.json').version"`
@@ -13,49 +14,56 @@ current_version := `node -p "require('./package.json').version"`
default:
just --list
+# Install the shared Purview.Build tool (authenticated to the Purview-Dev feed) if not present
+[private]
+ensure-pipeline-tool:
+ if [ ! -x "{{ pipeline_tool }}" ]; then \
+ dotnet tool install Purview.Build --tool-path .tools/purview-build --add-source "{{ pipeline_feed }}" --version "{{ pipeline_version }}"; \
+ fi
+
# Run the PR pipeline (restore, build, lint, tests)
[group('Pipeline')]
pipeline-pr *args:
+ just ensure-pipeline-tool
echo "Running PR pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} {{ args }}
+ "{{ pipeline_tool }}" {{ args }}
# Run the build pipeline (restore, build, lint)
[group('Pipeline')]
pipeline-build *args:
+ just ensure-pipeline-tool
echo "Running build pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Build:RunTests=false --Release:Mode=None {{ args }}
+ "{{ pipeline_tool }}" --Build:RunTests=false --Release:Mode=None {{ args }}
# Run the release pipeline (restore, build, lint, tests, pack, publish, GitHub release)
[group('Pipeline')]
pipeline-release *args:
+ just ensure-pipeline-tool
echo "Running release pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Release:Mode=NuGet {{ args }}
+ "{{ pipeline_tool }}" --Release:Mode=NuGet {{ args }}
# Run the release pipeline (restore, build, lint, tests, pack, local nuget publish)
# Note: `just` runs recipes through the shell, which strips backslashes from unquoted arguments.
-# Always use forward slashes for the feed path, e.g.
+# Use the LOCAL_NUGET_FEED_PATH environment variable or forward slashes, e.g.
# just pipeline-local-release --PublishLocalNuGet:LocalFeedPath=p:/_sync-projects/.local-nuget/
[group('Pipeline')]
pipeline-local-release *args:
+ just ensure-pipeline-tool
echo "Running local release pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Release:Mode=LocalNuGet {{ args }}
+ "{{ pipeline_tool }}" --Release:Mode=LocalNuGet {{ args }}
# Run the pipeline with tests enabled
[group('Pipeline')]
pipeline-tests *args:
+ just ensure-pipeline-tool
echo "Running tests pipeline..."
- dotnet run --project {{ pipeline_project }} --configuration {{ build_configuration }} -- --Build:RunTests=true --Release:Mode=None {{ args }}
+ "{{ pipeline_tool }}" --Build:RunTests=true --Release:Mode=None {{ args }}
# Open the solution in Visual Studio/ Registered application
[group('Utilities')]
vs:
open {{ solution_file }}
-# Open the solution in Visual Studio/ Registered application
-[group('Utilities')]
-vs-pipeline:
- open {{ pipeline_solution }}
-
# Build the solution for the specified configuration (default: Release)
[group('Build and Test')]
build *args:
diff --git a/build/Directory.Build.props b/build/Directory.Build.props
deleted file mode 100644
index c0da58b..0000000
--- a/build/Directory.Build.props
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- Purview.Aspire.ResourceKit
- true
-
-
-
-
-
- $(NoWarn);CA1062;CA1515;CA2007;CA1873;
-
-
diff --git a/build/Directory.Build.targets b/build/Directory.Build.targets
deleted file mode 100644
index 6d9a3e0..0000000
--- a/build/Directory.Build.targets
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/build/Directory.Packages.props b/build/Directory.Packages.props
deleted file mode 100644
index 4741f63..0000000
--- a/build/Directory.Packages.props
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
- [3.2.8,)
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/Pipeline.slnx b/build/Pipeline.slnx
deleted file mode 100644
index 5c5b98f..0000000
--- a/build/Pipeline.slnx
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/PipelineCLI/GlobalUsings.cs b/build/PipelineCLI/GlobalUsings.cs
deleted file mode 100644
index 0835bee..0000000
--- a/build/PipelineCLI/GlobalUsings.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-global using Microsoft.Extensions.Configuration;
-global using Microsoft.Extensions.DependencyInjection;
-global using Microsoft.Extensions.Logging;
-global using Microsoft.Extensions.Options;
-global using ModularPipelines;
-global using ModularPipelines.Extensions;
-global using Octokit;
-global using Octokit.Internal;
-global using Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
-global using Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-global using Purview.Aspire.ResourceKit.PipelineCLI.Settings;
diff --git a/build/PipelineCLI/Helpers/DotNetCLIOptions.cs b/build/PipelineCLI/Helpers/DotNetCLIOptions.cs
deleted file mode 100644
index 7f8dc73..0000000
--- a/build/PipelineCLI/Helpers/DotNetCLIOptions.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using ModularPipelines.Options;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
-
-public sealed record DotNetCLIOptions : CommandLineToolOptions
-{
- public static DotNetCLIOptions Create(params string[] commandParts) =>
- new() { Tool = "dotnet", CommandParts = commandParts };
-}
diff --git a/build/PipelineCLI/Helpers/PathHelpers.cs b/build/PipelineCLI/Helpers/PathHelpers.cs
deleted file mode 100644
index 36e8ba9..0000000
--- a/build/PipelineCLI/Helpers/PathHelpers.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
-
-static class PathHelpers
-{
- public static string FindRepositoryRoot(string? startDirectory = null)
- {
- if (string.IsNullOrEmpty(startDirectory))
- startDirectory = PipelineProjectDirectory.Find();
-
- DirectoryInfo? directory = new(startDirectory);
- while (directory is not null)
- {
- if (File.Exists(Path.Combine(directory.FullName, "package.json")))
- return directory.FullName;
-
- directory = directory.Parent;
- }
-
- throw new InvalidOperationException("Could not locate the repository root (no package.json found).");
- }
-}
diff --git a/build/PipelineCLI/Helpers/TestHelpers.cs b/build/PipelineCLI/Helpers/TestHelpers.cs
deleted file mode 100644
index c5eaa1a..0000000
--- a/build/PipelineCLI/Helpers/TestHelpers.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
-
-static class TestHelpers
-{
- public static string BuildTUnitTreeNodeFilter(
- string? assembly = null,
- string? @namespace = null,
- string? className = null,
- string? testNameQuery = null
- )
- {
- var filter = "/";
- filter += assembly switch
- {
- null => "*",
- _ => assembly,
- };
-
- filter += @namespace switch
- {
- null => "*",
- _ => @namespace,
- };
-
- filter += className switch
- {
- null => "*",
- _ => className,
- };
-
- filter += testNameQuery switch
- {
- null => "*",
- _ => testNameQuery,
- };
-
- return filter;
- }
-}
diff --git a/build/PipelineCLI/Modules/BuildModule.cs b/build/PipelineCLI/Modules/BuildModule.cs
deleted file mode 100644
index fddc45b..0000000
--- a/build/PipelineCLI/Modules/BuildModule.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-public class BuildModule(IOptions settings) : Module
-{
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- return await context
- .DotNet()
- .Build(
- new()
- {
- ProjectSolution = settings.Value.Solution,
- Configuration = settings.Value.Configuration,
- NoRestore = true,
- },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs b/build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs
deleted file mode 100644
index 6d24f31..0000000
--- a/build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.GitHub.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Release")]
-[DependsOn]
-[DependsOn]
-public class CreateGitHubReleaseModule(IOptions releaseSettings, IOptions gitSettings)
- : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- releaseSettings.Value.Mode is not (ReleaseMode.NuGet or ReleaseMode.GitHubRelease)
- || string.IsNullOrWhiteSpace(gitSettings.Value.GetGitHubToken())
- ? SkipDecision.Skip(
- "GitHub release creation is disabled. Set Release__Mode=NuGet (or GitHubRelease) and GITHUB_TOKEN to create a GitHub release."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
- {
- var versionResult = await context.GetModule();
- var version =
- versionResult.ValueOrDefault
- ?? throw new InvalidOperationException("The version was not produced by the version module.");
-
- var tag = $"v{version}";
-
- var repositoryIdString = context.GitHub().EnvironmentVariables.RepositoryId;
- if (!long.TryParse(repositoryIdString, out var repositoryId))
- {
- throw new InvalidOperationException(
- $"Failed to parse RepositoryId '{repositoryIdString}' as a valid long integer."
- );
- }
-
- // Create a new release on GitHub with the specified tag and generate release notes
- return await context
- .GitHub()
- .Client.Repository.Release.Create(
- repositoryId,
- new NewRelease(tag) { Name = tag, GenerateReleaseNotes = true }
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/LintModule.cs b/build/PipelineCLI/Modules/LintModule.cs
deleted file mode 100644
index 4511e1b..0000000
--- a/build/PipelineCLI/Modules/LintModule.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-public sealed class LintModule(IOptions settings) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- settings.Value.RunLint
- ? SkipDecision.DoNotSkip
- : SkipDecision.Skip("Linting is disabled. Set Build__RunLint=true to enable it.")
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var repositoryRoot = PathHelpers.FindRepositoryRoot();
- var dotnet = context.DotNet();
- var restoreResult = await dotnet.Tool.Restore(
- new() { Interactive = false, ToolManifest = Path.Combine(repositoryRoot, ".config", "dotnet-tools.json") },
- new() { WorkingDirectory = repositoryRoot },
- cancellationToken
- );
- if (restoreResult.ExitCode != 0)
- return restoreResult;
-
- // Restore worked, now run the linter
- return await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("tool", "run", "csharpier", "check", repositoryRoot),
- new() { WorkingDirectory = repositoryRoot },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/PackModule.cs b/build/PipelineCLI/Modules/PackModule.cs
deleted file mode 100644
index 27119b6..0000000
--- a/build/PipelineCLI/Modules/PackModule.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-[DependsOn]
-public sealed class PackModule(IOptions settings, IOptions releaseSettings)
- : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- !settings.Value.RunPack || releaseSettings.Value.Mode == ReleaseMode.None
- ? SkipDecision.Skip(
- "Packing is disabled. Set Build__RunPack=true and Release__Mode to something other than None to enable it."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var versionResult = await context.GetModule();
- var nugetVersion =
- versionResult.ValueOrDefault
- ?? throw new InvalidOperationException("The version was not produced by the version module.");
-
- Directory.CreateDirectory(settings.Value.ArtifactsFolder);
-
- var version = nugetVersion.ToString();
- return await context
- .DotNet()
- .Pack(
- new DotNetPackOptions
- {
- ProjectSolution = settings.Value.Solution,
- Configuration = settings.Value.Configuration,
- Output = settings.Value.ArtifactsFolder,
- Properties = [("PackageVersion", version), ("Version", version)],
- },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/PublishLocalNuGetModule.cs b/build/PipelineCLI/Modules/PublishLocalNuGetModule.cs
deleted file mode 100644
index 9374bc7..0000000
--- a/build/PipelineCLI/Modules/PublishLocalNuGetModule.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-using NuGet.Versioning;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-public class PublishLocalNuGetModule(
- IOptions localNuGetFeedSettings,
- IOptions releaseSettings,
- IOptions buildSettings
-) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(ctx =>
- !ctx.IsRunningLocally() || releaseSettings.Value.Mode != ReleaseMode.LocalNuGet
- ? SkipDecision.Skip(
- "Local NuGet Feed publishing is disabled. Run the pipeline locally with Release__Mode=LocalNuGet to enable it."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var localFeedPath = localNuGetFeedSettings.Value.LocalFeedPath;
-
- var validationResults = new List();
- var validationContext = new ValidationContext(localNuGetFeedSettings.Value);
- if (
- !Validator.TryValidateObject(
- localNuGetFeedSettings.Value,
- validationContext,
- validationResults,
- validateAllProperties: true
- )
- )
- {
- foreach (var validationResult in validationResults)
- context.Logger.LogError("{Message}", validationResult.ErrorMessage);
-
- throw new InvalidOperationException(
- $"Invalid {nameof(PublishLocalNuGetSettings)} configuration for {nameof(PublishLocalNuGetSettings.LocalFeedPath)}. "
- + "Windows paths with backslashes may have been stripped by the shell; "
- + "use forward slashes, e.g. --PublishLocalNuGet:LocalFeedPath=p:/_sync-projects/.local-nuget/."
- );
- }
-
- var fullLocalFeedPath = Path.GetFullPath(localFeedPath);
- context.Logger.LogInformation("Publishing local NuGet packages to {LocalFeedPath}.", fullLocalFeedPath);
-
- if (!Directory.Exists(fullLocalFeedPath))
- Directory.CreateDirectory(fullLocalFeedPath);
-
- var packages = Directory
- .GetFiles(buildSettings.Value.ArtifactsFolder, "*.nupkg")
- .Concat(Directory.GetFiles(buildSettings.Value.ArtifactsFolder, "*.snupkg"))
- .ToArray();
- if (packages.Length == 0)
- {
- throw new InvalidOperationException(
- $"No packages found in {buildSettings.Value.ArtifactsFolder}. The local feed was not populated."
- );
- }
-
- List nupkgPackages = [];
- foreach (var package in packages)
- {
- var fileName = Path.GetFileName(package);
- var destinationPath = Path.Combine(fullLocalFeedPath, fileName);
-
- if (Path.GetExtension(fileName) == ".nupkg")
- nupkgPackages.Add(await ParsePackageDetailsAsync(package, cancellationToken));
-
- if (!localNuGetFeedSettings.Value.OverwriteExistingPackages && File.Exists(destinationPath))
- {
- context.Logger.LogInformation("Package {Package} already exists in local feed. Skipping.", fileName);
- File.Delete(package);
-
- continue;
- }
-
- File.Move(package, destinationPath, true);
- context.Logger.LogInformation("Copied package {Package} to local feed.", fileName);
- }
-
- if (localNuGetFeedSettings.Value.ClearPackageCache)
- {
- context.Logger.LogInformation("Clearing local NuGet package cache...");
-
- var globalPackagesResult = await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("nuget", "locals", "global-packages", "--list"),
- cancellationToken: cancellationToken
- );
- if (globalPackagesResult.ExitCode != 0)
- return globalPackagesResult;
-
- var httpCacheResult = await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("nuget", "locals", "http-cache", "--list"),
- cancellationToken: cancellationToken
- );
- if (httpCacheResult.ExitCode != 0)
- return httpCacheResult;
-
- var globalPackagePaths = globalPackagesResult
- .StandardOutput.Replace("global-packages: ", "", StringComparison.Ordinal)
- .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)
- .Where(Directory.Exists);
-
- var httpCachePaths = httpCacheResult
- .StandardOutput.Replace("http-cache: ", "", StringComparison.Ordinal)
- .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries)
- .Where(Directory.Exists);
-
- foreach (var artifact in nupkgPackages)
- {
-#pragma warning disable CA1308 // Normalize strings to uppercase
- var loweredPackageId = artifact.PackageId.ToLowerInvariant();
- var loweredVersion = artifact.Version.ToFullString().ToLowerInvariant();
-#pragma warning restore CA1308 // Normalize strings to uppercase
-
- foreach (var globalPath in globalPackagePaths)
- {
- var packagePath = Path.Combine(globalPath, loweredPackageId, artifact.Version.ToFullString());
- if (Directory.Exists(packagePath))
- {
- Directory.Delete(packagePath, true);
- context.Logger.LogInformation(
- "Deleted package {Package} version {Version} from global packages cache.",
- artifact.PackageId,
- artifact.Version
- );
- }
- }
- foreach (var httpCachePath in httpCachePaths)
- {
- string[] packagePaths =
- [
- Path.Combine(httpCachePath, "list_" + loweredPackageId + ".dat"),
- Path.Combine(httpCachePath, "list_" + loweredPackageId + "_index.dat"),
- Path.Combine(httpCachePath, "list_" + loweredPackageId + "_range_*.dat"),
- Path.Combine(httpCachePath, "nupkg_" + loweredPackageId + "." + loweredVersion + ".dat"),
- ];
-
- foreach (var path in packagePaths)
- {
- var directory = Path.GetDirectoryName(path);
- var pattern = Path.GetFileName(path);
- foreach (var file in Directory.EnumerateFiles(directory!, pattern, SearchOption.AllDirectories))
- {
- File.Delete(file);
- context.Logger.LogInformation(
- "Deleted package {Package} version {Version} from HTTP cache.",
- artifact.PackageId,
- artifact.Version
- );
- }
- }
- }
- }
- }
-
- if (localNuGetFeedSettings.Value.ShutdownDotnetBuilderServer)
- {
- context.Logger.LogInformation("Shutting down dotnet builder server...");
-
- return await context.Shell.Command.ExecuteCommandLineTool(
- DotNetCLIOptions.Create("build-server", "shutdown"),
- cancellationToken: cancellationToken
- );
- }
-
- return null;
- }
-
- static async Task ParsePackageDetailsAsync(string artifact, CancellationToken cancellationToken)
- {
- using var packageReader = new NuGet.Packaging.PackageArchiveReader(artifact);
- var packaging = await packageReader.GetNuspecReaderAsync(cancellationToken);
-
- return new(packaging.GetId(), packaging.GetVersion());
- }
-}
-
-record struct PackageDetails(string PackageId, NuGetVersion Version);
diff --git a/build/PipelineCLI/Modules/PublishNuGetModule.cs b/build/PipelineCLI/Modules/PublishNuGetModule.cs
deleted file mode 100644
index 9c8f74e..0000000
--- a/build/PipelineCLI/Modules/PublishNuGetModule.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Release")]
-[DependsOn]
-[DependsOn]
-public class PublishNuGetModule(
- IOptions buildSettings,
- IOptions nugetSettings,
- IOptions releaseSettings
-) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- releaseSettings.Value.Mode != ReleaseMode.NuGet
- || string.IsNullOrWhiteSpace(nugetSettings.Value.GetNuGetAPIKey())
- ? SkipDecision.Skip(
- "NuGet publishing is disabled. Set Release__Mode=NuGet and NuGet__ApiKey (or NUGET_APIKEY) to publish packages to nuget.org."
- )
- : SkipDecision.DoNotSkip
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var artifactsFolder = buildSettings.Value.ArtifactsFolder;
- if (!Directory.Exists(artifactsFolder))
- {
- throw new InvalidOperationException(
- $"The artifacts folder '{artifactsFolder}' does not exist. "
- + "Ensure the pack step ran (Release__Mode must not be None) before publishing."
- );
- }
-
- var packages = Directory.EnumerateFiles(artifactsFolder, "*.nupkg", SearchOption.TopDirectoryOnly).ToList();
-
- if (packages.Count == 0)
- {
- throw new InvalidOperationException($"No NuGet packages found in {buildSettings.Value.ArtifactsFolder}.");
- }
-
- var tasks = packages.Select(package =>
- context
- .DotNet()
- .Nuget.Push(
- new()
- {
- Path = package,
- Source = nugetSettings.Value.FeedUrl,
- ApiKey = nugetSettings.Value.GetNuGetAPIKey(),
- SkipDuplicate = true,
- },
- cancellationToken: cancellationToken
- )
- );
-
- return await Task.WhenAll(tasks);
- }
-}
diff --git a/build/PipelineCLI/Modules/RestoreModule.cs b/build/PipelineCLI/Modules/RestoreModule.cs
deleted file mode 100644
index 139883c..0000000
--- a/build/PipelineCLI/Modules/RestoreModule.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-public class RestoreModule(IOptions settings) : Module
-{
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- return await context
- .DotNet()
- .Restore(
- new DotNetRestoreOptions { ProjectSolution = settings.Value.Solution },
- cancellationToken: cancellationToken
- );
- }
-}
diff --git a/build/PipelineCLI/Modules/RunTestsModule.cs b/build/PipelineCLI/Modules/RunTestsModule.cs
deleted file mode 100644
index d9c2030..0000000
--- a/build/PipelineCLI/Modules/RunTestsModule.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System.Diagnostics;
-using System.Text.RegularExpressions;
-using ModularPipelines.Attributes;
-using ModularPipelines.Configuration;
-using ModularPipelines.Context;
-using ModularPipelines.DotNet.Extensions;
-using ModularPipelines.DotNet.Options;
-using ModularPipelines.Models;
-using ModularPipelines.Modules;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-[DependsOn]
-public class RunTestsModule(IOptions settings) : Module
-{
- protected override ModuleConfiguration Configure() =>
- ModuleConfiguration
- .Create()
- .WithSkipWhen(_ =>
- settings.Value.RunTests
- ? SkipDecision.DoNotSkip
- : SkipDecision.Skip("Tests are disabled. Set Build__RunTests=true to run them.")
- )
- .Build();
-
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var testProjects = FilterTestProjects(
- Directory.EnumerateFiles("src/tests", "*Tests.csproj", SearchOption.AllDirectories).ToList(),
- settings.Value.TestProjects
- );
- if (testProjects.Count == 0)
- {
- context.Logger.LogWarning(
- "No test projects matched 'src/tests' (filter: {TestProjects}), despite tests being enabled. Skipping test execution.",
- settings.Value.TestProjects
- );
-
- return [];
- }
-
- var timings = new List<(string Project, TimeSpan Elapsed, int ExitCode)>();
-
- var tasks = testProjects.Select(async project =>
- {
- var stopwatch = Stopwatch.StartNew();
- var result = await context
- .DotNet()
- .Test(
- new DotNetTestOptions
- {
- Project = project,
- Configuration = settings.Value.Configuration,
- NoBuild = true,
- NoRestore = true,
- Arguments = ["--ignore-exit-code", "8", "--treenode-filter", settings.Value.TestFilter],
- },
- cancellationToken: cancellationToken
- );
- stopwatch.Stop();
-
- lock (timings)
- timings.Add((project, stopwatch.Elapsed, result.ExitCode));
-
- return result;
- });
-
- var results = await Task.WhenAll(tasks);
-
- context.Logger.LogInformation(
- "Test run timings:{NewLine}{Timings}",
- Environment.NewLine,
- string.Join(
- Environment.NewLine,
- timings
- .OrderByDescending(t => t.Elapsed)
- .Select(t => $" {Path.GetFileName(t.Project)}: {t.Elapsed.TotalSeconds:F1}s (exit {t.ExitCode})")
- )
- );
-
- return results;
- }
-
- static IReadOnlyList FilterTestProjects(IReadOnlyList projects, string filter)
- {
- if (string.IsNullOrWhiteSpace(filter) || filter.Trim() == "*")
- return projects;
-
- var patterns = filter
- .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
- .Select(ToRegexPattern)
- .ToArray();
-
- return projects
- .Where(project =>
- {
- var fileName = Path.GetFileName(project);
- return patterns.Any(pattern => Regex.IsMatch(fileName, pattern, RegexOptions.IgnoreCase));
- })
- .ToList();
- }
-
- static string ToRegexPattern(string entry)
- {
- if (entry.Contains('*', StringComparison.Ordinal))
- {
- var escaped = Regex.Escape(entry);
- return "^" + escaped.Replace("\\*", ".*", StringComparison.Ordinal) + "$";
- }
-
- return "^" + Regex.Escape(entry) + "$";
- }
-}
diff --git a/build/PipelineCLI/Modules/VersionModule.cs b/build/PipelineCLI/Modules/VersionModule.cs
deleted file mode 100644
index 80aa59f..0000000
--- a/build/PipelineCLI/Modules/VersionModule.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Text.Json;
-using ModularPipelines.Attributes;
-using ModularPipelines.Context;
-using ModularPipelines.Modules;
-using NuGet.Versioning;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
-
-[ModuleCategory("Build")]
-public class VersionModule : Module
-{
- protected override async Task ExecuteAsync(
- IModuleContext context,
- CancellationToken cancellationToken
- )
- {
- var packageJsonPath = Path.Combine(Environment.CurrentDirectory, "package.json");
-
- if (!File.Exists(packageJsonPath))
- throw new FileNotFoundException($"Could not find package.json at {packageJsonPath}");
-
- var packageJson = await File.ReadAllTextAsync(packageJsonPath, cancellationToken);
-
- using var document = JsonDocument.Parse(packageJson);
- var version = document.RootElement.GetProperty("version").GetString();
-
- if (string.IsNullOrWhiteSpace(version))
- throw new InvalidOperationException("The version field in package.json is missing or empty.");
-
- if (!NuGetVersion.TryParse(version, out var nugetVersion))
- throw new InvalidOperationException($"The version '{version}' in package.json is not a valid SemVer.");
-
- context.Summary.KeyValue("Version", "Package version", version);
- return nugetVersion;
- }
-}
diff --git a/build/PipelineCLI/PipelineCLI.csproj b/build/PipelineCLI/PipelineCLI.csproj
deleted file mode 100644
index 0264da8..0000000
--- a/build/PipelineCLI/PipelineCLI.csproj
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
diff --git a/build/PipelineCLI/PipelineProjectDirectory.cs b/build/PipelineCLI/PipelineProjectDirectory.cs
deleted file mode 100644
index 288cda8..0000000
--- a/build/PipelineCLI/PipelineProjectDirectory.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI;
-
-static class PipelineProjectDirectory
-{
- const string DirectoryVariable = "MODULAR_PIPELINES_DIRECTORY";
-
- public static string Find([CallerFilePath] string sourceFilePath = "")
- {
- var configuredDirectory = Environment.GetEnvironmentVariable(DirectoryVariable);
- if (!string.IsNullOrWhiteSpace(configuredDirectory))
- {
- return ValidateConfiguredDirectory(configuredDirectory);
- }
-
- var sourceDirectory = Path.GetDirectoryName(sourceFilePath);
- return IsPipelineDirectory(sourceDirectory) ? sourceDirectory! : FindFromBuildOutput();
- }
-
- static string ValidateConfiguredDirectory(string configuredDirectory)
- {
- var fullPath = Path.GetFullPath(configuredDirectory);
- return IsPipelineDirectory(fullPath)
- ? fullPath
- : throw new InvalidOperationException(
- $"{DirectoryVariable} must point to a directory containing appsettings.json and a project file."
- );
- }
-
- static string FindFromBuildOutput()
- {
- for (
- var directory = new DirectoryInfo(AppContext.BaseDirectory);
- directory is not null;
- directory = directory.Parent
- )
- {
- if (IsPipelineDirectory(directory.FullName))
- {
- return directory.FullName;
- }
- }
-
- throw new InvalidOperationException(
- $"Could not locate the pipeline project directory. Set {DirectoryVariable} to its path."
- );
- }
-
- static bool IsPipelineDirectory(string? directory) =>
- directory is not null
- && Directory.Exists(directory)
- && File.Exists(Path.Combine(directory, "appsettings.json"))
- && Directory.EnumerateFiles(directory, "*.csproj").Any();
-}
diff --git a/build/PipelineCLI/Program.cs b/build/PipelineCLI/Program.cs
deleted file mode 100644
index 5d38362..0000000
--- a/build/PipelineCLI/Program.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-var pipelineDirectory = PipelineProjectDirectory.Find();
-var repositoryRoot = PathHelpers.FindRepositoryRoot(pipelineDirectory);
-
-var builder = Pipeline.CreateBuilder(args);
-
-builder
- .Configuration.AddJsonFile(Path.Combine(pipelineDirectory, "appsettings.json"), optional: false)
- .AddEnvironmentVariables()
- .AddCommandLine(args);
-
-builder.Services.Configure(builder.Configuration.GetSection(BuildSettings.SectionName));
-builder.Services.Configure(builder.Configuration.GetSection(NuGetSettings.SectionName));
-builder.Services.Configure(
- builder.Configuration.GetSection(PublishLocalNuGetSettings.SectionName)
-);
-builder.Services.Configure(builder.Configuration.GetSection(GitHubSettings.SectionName));
-builder.Services.Configure(builder.Configuration.GetSection(ReleaseSettings.SectionName));
-
-builder.Services.AddSingleton(serviceProvider =>
-{
- var settings = serviceProvider.GetRequiredService>();
- var accessToken = settings.Value.GetGitHubToken();
-
- return new GitHubClient(new(settings.Value.ProductHeader), new InMemoryCredentialStore(new(accessToken)));
-});
-
-Environment.CurrentDirectory = repositoryRoot;
-
-builder
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule()
- .AddModule();
-
-await using var pipeline = await builder.BuildAsync();
-
-await pipeline.RunAsync();
diff --git a/build/PipelineCLI/Properties/launchSettings.json b/build/PipelineCLI/Properties/launchSettings.json
deleted file mode 100644
index d5bbebe..0000000
--- a/build/PipelineCLI/Properties/launchSettings.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "profiles": {
- "Run": {
- "commandName": "Project"
- },
- "Local-NuGet": {
- "commandName": "Project",
- "commandLineArgs": "--Release:Mode=LocalNuGet\r\n--PublishLocalNuGet:LocalFeedPath=p:\\_sync-projects\\.local-nuget\\"
- }
- }
-}
diff --git a/build/PipelineCLI/Settings/BuildSettings.cs b/build/PipelineCLI/Settings/BuildSettings.cs
deleted file mode 100644
index 0a7f159..0000000
--- a/build/PipelineCLI/Settings/BuildSettings.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Settings;
-
-public sealed class BuildSettings
-{
- public const string SectionName = "Build";
-
- public LogLevel LogLevel { get; init; } = LogLevel.Warning;
-
- [Required(AllowEmptyStrings = false)]
- public string Solution { get; init; } = "src/DotNetProjectSdk.slnx";
-
- [Required(AllowEmptyStrings = false)]
- public string Configuration { get; init; } = "Release";
-
- [Required(AllowEmptyStrings = false)]
- public string ArtifactsFolder { get; init; } = "artifacts";
-
- public bool RunTests { get; init; } = true;
-
- [Required(AllowEmptyStrings = false)]
- public string TestFilter { get; init; } = "/*/*/*/*/";
-
- ///
- /// Comma-separated list of test project file names (or glob patterns) to run.
- /// Empty or "*" runs every test project under src/tests.
- ///
- public string TestProjects { get; init; } = "*";
-
- public bool RunLint { get; init; } = true;
-
- public bool RunPack { get; init; } = true;
-}
diff --git a/build/PipelineCLI/Settings/GitHubSettings.cs b/build/PipelineCLI/Settings/GitHubSettings.cs
deleted file mode 100644
index b82ed19..0000000
--- a/build/PipelineCLI/Settings/GitHubSettings.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using ModularPipelines.Attributes;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Settings;
-
-public sealed record GitHubSettings
-{
- public const string SectionName = "GitHub";
-
- [SecretValue]
- public string? AccessToken { get; init; }
-
- [SecretValue]
- [ConfigurationKeyName("GITHUB_TOKEN")]
- public string? EnvAccessToken { get; init; }
-
- public string ProductHeader { get; init; } = "Purview.SourceGeneratorFramework.Pipeline";
-
- public string? GetGitHubToken()
- {
- if (!string.IsNullOrWhiteSpace(AccessToken))
- return AccessToken;
-
- if (!string.IsNullOrWhiteSpace(EnvAccessToken))
- return EnvAccessToken;
-
- // GitHub Actions provisions the automatic GITHUB_TOKEN as a plain environment variable.
- // The config binder keys it under the "GitHub" section (GitHub:GITHUB_TOKEN), which the
- // standard GITHUB_TOKEN env var does not map to, so read it directly as a fallback.
- var processToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
- return string.IsNullOrWhiteSpace(processToken) ? null : processToken;
- }
-}
diff --git a/build/PipelineCLI/Settings/NuGetSettings.cs b/build/PipelineCLI/Settings/NuGetSettings.cs
deleted file mode 100644
index a2a530b..0000000
--- a/build/PipelineCLI/Settings/NuGetSettings.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using ModularPipelines.Attributes;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Settings;
-
-public sealed record NuGetSettings
-{
- public const string SectionName = "NuGet";
-
- [SecretValue]
- public string? APIKey { get; set; }
-
- [SecretValue]
- [ConfigurationKeyName("NUGET_APIKEY")]
- public string? EnvAPIKey { get; set; }
-
- public string FeedUrl { get; init; } = "https://api.nuget.org/v3/index.json";
-
- public string? GetNuGetAPIKey() =>
- !string.IsNullOrWhiteSpace(APIKey) ? APIKey
- : !string.IsNullOrWhiteSpace(EnvAPIKey) ? EnvAPIKey
- : null;
-}
diff --git a/build/PipelineCLI/Settings/PublishLocalNuGetSettings.cs b/build/PipelineCLI/Settings/PublishLocalNuGetSettings.cs
deleted file mode 100644
index 3c5514e..0000000
--- a/build/PipelineCLI/Settings/PublishLocalNuGetSettings.cs
+++ /dev/null
@@ -1,82 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Settings;
-
-public sealed record PublishLocalNuGetSettings : IValidatableObject
-{
- public const string SectionName = "PublishLocalNuGet";
-
- [Required(AllowEmptyStrings = false)]
- public string LocalFeedPath { get; init; } = string.Empty;
-
- public bool OverwriteExistingPackages { get; init; } = true;
-
- public bool ShutdownDotnetBuilderServer { get; init; } = true;
-
- public bool ClearPackageCache { get; init; } = true;
-
- public IEnumerable Validate(ValidationContext validationContext)
- {
- if (string.IsNullOrWhiteSpace(LocalFeedPath))
- {
- yield return new ValidationResult("LocalFeedPath is required.", [nameof(LocalFeedPath)]);
- yield break;
- }
-
- // Path.IsPathRooted("p:foo") returns true, but a drive-relative path like "p:foo" is NOT an
- // absolute path: Path.GetFullPath resolves it against the current directory and can silently
- // copy packages to an unintended location. This is the classic signature of a Windows path whose
- // backslashes were stripped by a sh-style shell, e.g. 'p:\_sync-projects\.local-nuget\'.
- if (LocalFeedPath.Length >= 2 && LocalFeedPath[1] == ':')
- {
- var hasSeparatorAfterDrive =
- LocalFeedPath.Length >= 3
- && (
- LocalFeedPath[2] == Path.DirectorySeparatorChar
- || LocalFeedPath[2] == Path.AltDirectorySeparatorChar
- );
- if (!hasSeparatorAfterDrive)
- {
- yield return new ValidationResult(
- $"LocalFeedPath '{LocalFeedPath}' is drive-relative, not an absolute path. "
- + "This is usually caused by the shell stripping backslashes from a Windows path such as "
- + $"'p:\\_sync-projects\\.local-nuget\\'. Use forward slashes instead, e.g. "
- + "'p:/_sync-projects/.local-nuget/'.",
- [nameof(LocalFeedPath)]
- );
- yield break;
- }
- }
-
- if (!Path.IsPathRooted(LocalFeedPath))
- {
- yield return new ValidationResult(
- $"LocalFeedPath must be an absolute path. Received: '{LocalFeedPath}'.",
- [nameof(LocalFeedPath)]
- );
- yield break;
- }
-
- var root = Path.GetPathRoot(LocalFeedPath);
- if (string.IsNullOrEmpty(root))
- {
- yield return new ValidationResult(
- $"LocalFeedPath could not be parsed. Received: '{LocalFeedPath}'.",
- [nameof(LocalFeedPath)]
- );
- yield break;
- }
-
- var lastChar = root[^1];
- if (lastChar == Path.DirectorySeparatorChar || lastChar == Path.AltDirectorySeparatorChar)
- yield break;
-
- if (root.StartsWith(@"\\", StringComparison.Ordinal) || root.StartsWith("//", StringComparison.Ordinal))
- yield break;
-
- yield return new ValidationResult(
- $"LocalFeedPath must be an absolute path (e.g. 'C:\\folder' or '\\\\server\\share'). Received: '{LocalFeedPath}'.",
- [nameof(LocalFeedPath)]
- );
- }
-}
diff --git a/build/PipelineCLI/Settings/ReleaseSettings.cs b/build/PipelineCLI/Settings/ReleaseSettings.cs
deleted file mode 100644
index 69fa2a7..0000000
--- a/build/PipelineCLI/Settings/ReleaseSettings.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Purview.Aspire.ResourceKit.PipelineCLI.Settings;
-
-public enum ReleaseMode
-{
- None,
-
- NuGet,
-
- GitHubRelease,
-
- LocalNuGet,
-}
-
-public sealed record ReleaseSettings
-{
- public const string SectionName = "Release";
-
- public ReleaseMode Mode { get; set; } = ReleaseMode.None;
-}
diff --git a/build/PipelineCLI/appsettings.json b/build/PipelineCLI/appsettings.json
deleted file mode 100644
index 9381c34..0000000
--- a/build/PipelineCLI/appsettings.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "NuGet": {
- "FeedUrl": "https://api.nuget.org/v3/index.json"
- },
- "GitHub": {
- "AccessToken": null,
- "ProductHeader": "Purview.SourceGeneratorFramework.Pipeline"
- },
- "Release": {
- "Mode": "None"
- }
-}
diff --git a/package.json b/package.json
index dcb9d9d..e85fef2 100644
--- a/package.json
+++ b/package.json
@@ -1,4 +1,4 @@
{
"name": "pruview-dotnet-project-sdk",
- "version": "1.0.0-prerelease.46"
+ "version": "1.0.0-prerelease.47"
}
diff --git a/purview-build.json b/purview-build.json
new file mode 100644
index 0000000..3632808
--- /dev/null
+++ b/purview-build.json
@@ -0,0 +1,14 @@
+{
+ "Build": {
+ "Solution": "src/DotNetProjectSdk.slnx",
+ "TestRoot": "src/tests",
+ "TestPatterns": "*Tests.csproj",
+ "TestFilter": "/*/*/*/*/"
+ },
+ "PackValidation": {
+ "RequireSymbolPackage": false
+ },
+ "Release": {
+ "Mode": "None"
+ }
+}
\ No newline at end of file