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
26 changes: 25 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **BREAKING CHANGE**: `bv clean` (formerly known as `bv prepare`) now deletes the `TestResults` directory at the repository root.
- `bv clean` (formerly known as `bv prepare`) no longer deletes per-project `TestResults` directories.
- `dotnet bv release` no longer folds self-reference (dogfood) updates into the "Prepare release" commit. They now go into a separate `Update self-references to <version> [skip ci]` commit pushed on top, in the same push. The release tag binds to the "Prepare release" commit, so checking out the tag and rebuilding now reproduces the actually-released source state (which still references the previously-published versions). `[skip ci]` is required on the dogfood commit because the new packages are usually not yet published at push time.
- **BREAKING CHANGE**: Five `bv release` options have been renamed: `--versionSpecChange` → `--bump`, `--checkPublicApiFiles` → `--check-public-api`, `--updateChangelogOnPrerelease` → `--unstable-changelog`, `--ensureChangelogNotEmpty` → `--require-changelog`, `--updateSelfReferences` → `--dogfood`. The matching default-source environment variables follow the same renaming.
- **BREAKING CHANGE**: Five `bv release` options have been renamed:
- `--versionSpecChange` → `--bump`
- `--checkPublicApiFiles` → `--check-public-api`
- `--updateChangelogOnPrerelease` → `--unstable-changelog`
- `--ensureChangelogNotEmpty` → `--require-changelog`
- `--updateSelfReferences` → `--dogfood`
- **BREAKING CHANGE**: `bv` no longer accepts CLI option values via environment variables. The following env vars are no longer recognized as defaults for their CLI counterparts:
- `CONFIGURATION` (`--configuration`)
- `MAIN_BRANCH` (`--main-branch`)
- `VERSION_SPEC_CHANGE` (`--versionSpecChange`, now `--bump`)
- `CHECK_PUBLIC_API_FILES` (`--checkPublicApiFiles`, now `--check-public-api`)
- `UPDATE_CHANGELOG_ON_PRERELEASE` (`--updateChangelogOnPrerelease`, now `--unstable-changelog`)
- `ENSURE_CHANGELOG_NOT_EMPTY` (`--ensureChangelogNotEmpty`, now `--require-changelog`)
- `UPDATE_SELF_REFERENCES` (`--updateSelfReferences`, now `--dogfood`)

Pass values via CLI flags instead.
Secrets and endpoints (`GITHUB_TOKEN`, `PRIVATE_NUGET_SOURCE`/`KEY`, `PRERELEASE_NUGET_SOURCE`/`KEY`, `RELEASE_NUGET_SOURCE`/`KEY`) are unaffected.
- **BREAKING CHANGE**: `bv`'s `--verbosity` setting now accepts the same values as the .NET CLI:
- `quiet` (or `q`)
- `minimal` (or `m`)
- `normal` (or `n`)
- `detailed` (or `d`)
- `diagnostic` (or `diag`)

Cake verbosity values (e.g., `verbose`) are no longer accepted.

### Bugs fixed in this release

Expand Down
55 changes: 55 additions & 0 deletions src/Buildvana.Tool/Cli/BaseSettings-Apply.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (C) Tenacom and Contributors. Licensed under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using Buildvana.Core;
using Buildvana.Tool.Infrastructure.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Spectre.Console;

namespace Buildvana.Tool.Cli;

partial class BaseSettings
{
/// <summary>
/// Applies the global options carried by this settings object to the runtime services:
/// logger min level (from <see cref="Verbosity"/>) and console color profile (from <see cref="Color"/>/<see cref="NoColor"/>).
/// </summary>
/// <param name="services">The service provider.</param>
public void Apply(IServiceProvider services)
{
ApplyVerbosity(services);
ApplyColor(services);
}

private static LogLevel ParseVerbosity(string raw) => raw.ToUpperInvariant() switch
{
"QUIET" or "Q" => LogLevel.Error,
"MINIMAL" or "M" => LogLevel.Warning,
"NORMAL" or "N" => LogLevel.Information,
"DETAILED" or "D" => LogLevel.Debug,
"DIAGNOSTIC" or "DIAG" => LogLevel.Trace,
_ => throw new BuildFailedException($"Unknown verbosity level '{raw}'. Use one of: quiet, minimal, normal, detailed, diagnostic."),
};

private void ApplyVerbosity(IServiceProvider services)
{
if (string.IsNullOrEmpty(Verbosity))
{
return;
}

services.GetRequiredService<SpectreLoggerProvider>().MinLevel = ParseVerbosity(Verbosity);
}

private void ApplyColor(IServiceProvider services)
{
if (Color == NoColor)
{
return;
}

services.GetRequiredService<IAnsiConsole>().Profile.Capabilities.Ansi = Color;
}
}
4 changes: 2 additions & 2 deletions src/Buildvana.Tool/Cli/BaseSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ namespace Buildvana.Tool.Cli;
/// Global options shared by every Spectre command.
/// </summary>
[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
public class BaseSettings : CommandSettings
public partial class BaseSettings : CommandSettings
{
/// <summary>
/// Gets the requested logging verbosity.
/// </summary>
[CommandOption("-v|--verbosity <LEVEL>")]
[Description("Logging verbosity. Accepts either Trace/Debug/Information/Warning/Error/Critical/None or Quiet/Minimal/Normal/Verbose/Diagnostic.")]
[Description("Logging verbosity. One of: quiet, minimal, normal, detailed, diagnostic. Defaults to normal.")]
public string? Verbosity { get; init; }

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Buildvana.Tool/Cli/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;

namespace Buildvana.Tool.Cli;
Expand All @@ -16,7 +17,8 @@ internal sealed class BuildCommand(IServiceProvider services) : AsyncCommand<Bui
protected override async Task<int> ExecuteAsync(CommandContext context, BuildSettings settings, CancellationToken cancellationToken)
{
Guard.IsNotNull(settings);
SettingsApplier.Apply(settings, services);
settings.Apply(services);
services.GetRequiredService<BuildSettingsHolder>().Current = settings;
await BuildSteps.CleanAsync(services).ConfigureAwait(false);
await BuildSteps.RestoreAsync(services).ConfigureAwait(false);
await BuildSteps.BuildAsync(services).ConfigureAwait(false);
Expand Down
15 changes: 13 additions & 2 deletions src/Buildvana.Tool/Cli/BuildSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,24 @@ public class BuildSettings : BaseSettings
/// Gets the MSBuild configuration to build.
/// </summary>
[CommandOption("-c|--configuration <NAME>")]
[Description("MSBuild configuration to build. Defaults to 'Release' (or the CONFIGURATION environment variable, if set).")]
[Description("MSBuild configuration to build. Defaults to 'Release'.")]
public string? Configuration { get; init; }

/// <summary>
/// Gets the name of the repository's main branch.
/// </summary>
[CommandOption("--main-branch <NAME>")]
[Description("Name of the repository's main branch. Defaults to 'main' (or the MAIN_BRANCH environment variable, if set).")]
[Description("Name of the repository's main branch. Defaults to 'main'.")]
public string? MainBranch { get; init; }

/// <summary>
/// Gets the resolved MSBuild configuration: <see cref="Configuration"/> if set, otherwise <c>"Release"</c>.
/// </summary>
public string ResolveConfiguration() => Configuration ?? "Release";

/// <summary>
/// Gets the configured main branch name, or the empty string if none was passed (in which case
/// <c>GitService</c> auto-detects from <c>main</c>/<c>master</c>).
/// </summary>
public string ResolveMainBranch() => MainBranch ?? string.Empty;
}
22 changes: 22 additions & 0 deletions src/Buildvana.Tool/Cli/BuildSettingsHolder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (C) Tenacom and Contributors. Licensed under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;

namespace Buildvana.Tool.Cli;

/// <summary>
/// Singleton holder for the current command's <see cref="BuildSettings"/>.
/// </summary>
/// <remarks>
/// <para>The active command's <c>ExecuteAsync</c> populates <see cref="Current"/> before any service that
/// reads build options is resolved.</para>
/// </remarks>
public sealed class BuildSettingsHolder
{
public BuildSettings Current
{
get => field ?? throw new InvalidOperationException("BuildSettingsHolder.Current was read before it was set.");
set;
}
}
2 changes: 1 addition & 1 deletion src/Buildvana.Tool/Cli/CleanCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal sealed class CleanCommand(IServiceProvider services) : AsyncCommand<Bas
protected override async Task<int> ExecuteAsync(CommandContext context, BaseSettings settings, CancellationToken cancellationToken)
{
Guard.IsNotNull(settings);
SettingsApplier.Apply(settings, services);
settings.Apply(services);
await BuildSteps.CleanAsync(services).ConfigureAwait(false);
return 0;
}
Expand Down
4 changes: 3 additions & 1 deletion src/Buildvana.Tool/Cli/PackCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;

namespace Buildvana.Tool.Cli;
Expand All @@ -16,7 +17,8 @@ internal sealed class PackCommand(IServiceProvider services) : AsyncCommand<Buil
protected override async Task<int> ExecuteAsync(CommandContext context, BuildSettings settings, CancellationToken cancellationToken)
{
Guard.IsNotNull(settings);
SettingsApplier.Apply(settings, services);
settings.Apply(services);
services.GetRequiredService<BuildSettingsHolder>().Current = settings;
await BuildSteps.CleanAsync(services).ConfigureAwait(false);
await BuildSteps.RestoreAsync(services).ConfigureAwait(false);
await BuildSteps.BuildAsync(services).ConfigureAwait(false);
Expand Down
14 changes: 7 additions & 7 deletions src/Buildvana.Tool/Cli/ReleaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ internal sealed class ReleaseCommand(IServiceProvider services) : AsyncCommand<R
protected override async Task<int> ExecuteAsync(CommandContext context, ReleaseSettings settings, CancellationToken cancellationToken)
{
Guard.IsNotNull(settings);
SettingsApplier.Apply(settings, services);
settings.Apply(services);
services.GetRequiredService<BuildSettingsHolder>().Current = settings;

// Pre-pipeline (mirrors today's [IsDependentOn(TestTask)] chain).
await BuildSteps.CleanAsync(services).ConfigureAwait(false);
Expand All @@ -41,7 +42,6 @@ protected override async Task<int> ExecuteAsync(CommandContext context, ReleaseS
var logger = services.GetRequiredService<ILoggerFactory>().CreateLogger("Release");
var home = services.GetRequiredService<IHomeDirectoryProvider>();
var jsonHelper = services.GetRequiredService<IJsonHelper>();
var options = services.GetRequiredService<OptionsService>();
var server = services.GetRequiredService<ServerAdapter>();
var version = services.GetRequiredService<VersionService>();
var dotnet = services.GetRequiredService<DotNetService>();
Expand Down Expand Up @@ -82,8 +82,8 @@ protected override async Task<int> ExecuteAsync(CommandContext context, ReleaseS
// Compute the version spec change to apply, if any.
// This implies more checks and possibly throws, so do it as early as possible.
var versionSpecChange = version.ComputeVersionSpecChange(
requestedChange: options.GetOption("bump", VersionSpecChange.None),
checkPublicApiFiles: options.GetOption("checkPublicApi", true));
requestedChange: settings.ResolveBump(),
checkPublicApiFiles: settings.ResolveCheckPublicApi());

var release = await server.CreateReleaseAsync().ConfigureAwait(false);
await using (release.ConfigureAwait(false))
Expand Down Expand Up @@ -138,9 +138,9 @@ protected override async Task<int> ExecuteAsync(CommandContext context, ReleaseS
{
logger.LogInformation("Changelog update skipped: {Path} not found.", ChangelogService.FileName);
}
else if (!version.IsPrerelease || options.GetOption("unstableChangelog", false))
else if (!version.IsPrerelease || settings.ResolveUnstableChangelog())
{
if (options.GetOption("requireChangelog", true))
if (settings.ResolveRequireChangelog())
{
BuildFailedException.ThrowIfNot(
changelog.HasUnreleasedChanges(),
Expand Down Expand Up @@ -196,7 +196,7 @@ protected override async Task<int> ExecuteAsync(CommandContext context, ReleaseS
// Goes into a separate commit so the tagged "Prepare release" commit reflects the actual built
// state (which still references the previously-published versions); the dogfood commit is marked
// [skip ci] because the new packages aren't in the feed yet at push time.
if (options.GetOption("dogfood", true))
if (settings.ResolveDogfood())
{
var selfReferenceUpdates = selfReferenceUpdater.UpdateReferences();
switch (selfReferenceUpdates.Count)
Expand Down
40 changes: 40 additions & 0 deletions src/Buildvana.Tool/Cli/ReleaseSettings.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (C) Tenacom and Contributors. Licensed under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.ComponentModel;
using Buildvana.Core;
using Buildvana.Tool.Services.Versioning;
using JetBrains.Annotations;
using Spectre.Console.Cli;

Expand Down Expand Up @@ -54,4 +57,41 @@ public class ReleaseSettings : BuildSettings
[CommandOption("--dogfood <BOOL>")]
[Description("Update in-tree references to packages produced by this release. Defaults to true.")]
public bool? Dogfood { get; init; }

/// <summary>
/// Parses <see cref="Bump"/> into a <see cref="VersionSpecChange"/>; defaults to <see cref="VersionSpecChange.None"/>.
/// </summary>
/// <exception cref="BuildFailedException">The value of <see cref="Bump"/> is not a recognized version-spec change.</exception>
public VersionSpecChange ResolveBump()
{
if (Bump is null)
{
return VersionSpecChange.None;
}

var parsed = Enum.TryParse<VersionSpecChange>(Bump, ignoreCase: true, out var value) && Enum.IsDefined(value);
return parsed
? value
: throw new BuildFailedException($"Invalid value '{Bump}' for --bump. Valid values: none, unstable, stable, minor, major.");
}

/// <summary>
/// Returns <see cref="CheckPublicApi"/> if set, otherwise <see langword="true"/>.
/// </summary>
public bool ResolveCheckPublicApi() => CheckPublicApi ?? true;

/// <summary>
/// Returns <see cref="UnstableChangelog"/> if set, otherwise <see langword="false"/>.
/// </summary>
public bool ResolveUnstableChangelog() => UnstableChangelog ?? false;

/// <summary>
/// Returns <see cref="RequireChangelog"/> if set, otherwise <see langword="true"/>.
/// </summary>
public bool ResolveRequireChangelog() => RequireChangelog ?? true;

/// <summary>
/// Returns <see cref="Dogfood"/> if set, otherwise <see langword="true"/>.
/// </summary>
public bool ResolveDogfood() => Dogfood ?? true;
}
4 changes: 3 additions & 1 deletion src/Buildvana.Tool/Cli/RestoreCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;

namespace Buildvana.Tool.Cli;
Expand All @@ -16,7 +17,8 @@ internal sealed class RestoreCommand(IServiceProvider services) : AsyncCommand<B
protected override async Task<int> ExecuteAsync(CommandContext context, BuildSettings settings, CancellationToken cancellationToken)
{
Guard.IsNotNull(settings);
SettingsApplier.Apply(settings, services);
settings.Apply(services);
services.GetRequiredService<BuildSettingsHolder>().Current = settings;
await BuildSteps.CleanAsync(services).ConfigureAwait(false);
await BuildSteps.RestoreAsync(services).ConfigureAwait(false);
return 0;
Expand Down
Loading
Loading