From 571d11a33a755db7e6140947498f3f9cb7983528 Mon Sep 17 00:00:00 2001 From: Chrison Simtian Date: Thu, 21 May 2026 12:48:47 +1200 Subject: [PATCH 1/3] chore(rebrand): deprecate Nuke* MSBuild properties with FALLOUT001 warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #57. ## Properties renamed All consumer-facing MSBuild properties exposed by Fallout.Common.targets and Fallout.MSBuildTasks.targets are now Fallout*-prefixed. Legacy Nuke* names remain functional for the 10.x line — to be removed in 11.0. ### Consumer-set (17) NukeRootDirectory, NukeScriptDirectory, NukeTelemetryVersion, NukeBaseDirectory, NukeBaseNamespace, NukeUseNestedNamespaces, NukeRepositoryUrl, NukeUpdateReferences, NukeContinueOnError, NukeTaskTimeout / NukeTimeout, NukeTasksEnabled, NukeDefaultExcludes, NukeExcludeBoot / Config / Logs / DirectoryBuild / Ci, NukeSpecificationFiles (item group), NukeExternalFiles (item group). ### Internal (2) NukeTasksAssembly, NukeTasksDirectory. ## How the fallback works Each Fallout* prop is set in this order: 1. If FalloutX is already set, use it. 2. Else if legacy NukeX is set, lift its value into FalloutX. 3. Else apply the default. So an existing .csproj setting NukeBaseDirectory keeps producing the exact same effective FalloutBaseDirectory value. Targets internally reference only $(FalloutX). ## FALLOUT001 warning The Fallout.Common.targets file declares an ItemGroup of detected legacy properties and emits one MSBuild warning per detected item: warning FALLOUT001: MSBuild property 'NukeBaseDirectory' is deprecated; rename to 'FalloutBaseDirectory'. The legacy name still works in 10.x but will be removed in 11.0. Helps consumers spot every project that needs updating. ## Target name aliases Targets NukeExternalFiles, NukePackPackageTools, NukeEmbedPackagesForSelfContained, NukeCodeGeneration kept as no-op stubs that DependsOnTargets="FalloutX". Anyone using BeforeTargets="NukeCodeGeneration" keeps working. ## Dogfood Updated build/_build.csproj in this repo to use the new names — verified FALLOUT001 warnings no longer fire on our own build. ## Template update src/Fallout.GlobalTool/templates/_build.csproj now emits Fallout* names — fresh `dotnet fallout :setup` runs get the new world from day one. Co-Authored-By: Claude Opus 4.7 (1M context) --- build/_build.csproj | 8 +- src/Fallout.Common/Fallout.Common.targets | 131 +++++++++++++----- .../templates/_build.csproj | 6 +- .../Fallout.MSBuildTasks.targets | 85 +++++++++--- 4 files changed, 162 insertions(+), 68 deletions(-) diff --git a/build/_build.csproj b/build/_build.csproj index a0d49ec09..d746b89d6 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -8,16 +8,16 @@ preview CS0649;CS0169 - .\.. - 1 + .\.. + 1 false false - False - $(MSBuildThisFileDirectory)\..\src\Fallout.MSBuildTasks\bin\Debug\net8.0\publish + False + $(MSBuildThisFileDirectory)\..\src\Fallout.MSBuildTasks\bin\Debug\net8.0\publish diff --git a/src/Fallout.Common/Fallout.Common.targets b/src/Fallout.Common/Fallout.Common.targets index fb9b4ed4a..d1023c85a 100644 --- a/src/Fallout.Common/Fallout.Common.targets +++ b/src/Fallout.Common/Fallout.Common.targets @@ -1,63 +1,118 @@ - + - - $(MSBuildThisFileDirectory)\netcore - $(MSBuildThisFileDirectory)\netfx + + + $(NukeTasksDirectory) + $(NukeTasksEnabled) + $(NukeRootDirectory) + $(NukeScriptDirectory) + $(NukeTelemetryVersion) + $(NukeDefaultExcludes) + $(NukeExcludeBoot) + $(NukeExcludeConfig) + $(NukeExcludeLogs) + $(NukeExcludeDirectoryBuild) + $(NukeExcludeCi) + + + + $(MSBuildThisFileDirectory)\netcore + $(MSBuildThisFileDirectory)\netfx - + - $(NukeRootDirectory) + $(FalloutRootDirectory) - + - - - - + + + + - - - + + + - - - - - - - + + + + + + + - - + + - + - - + - - - - - - - - - - - + + + + + + + + + + + + + + <_LegacyFalloutProperty Include="NukeTasksAssembly" Condition="'$(NukeTasksAssembly)' != ''" >FalloutTasksAssembly + <_LegacyFalloutProperty Include="NukeTasksDirectory" Condition="'$(NukeTasksDirectory)' != ''" >FalloutTasksDirectory + <_LegacyFalloutProperty Include="NukeTasksEnabled" Condition="'$(NukeTasksEnabled)' != ''" >FalloutTasksEnabled + <_LegacyFalloutProperty Include="NukeRootDirectory" Condition="'$(NukeRootDirectory)' != ''" >FalloutRootDirectory + <_LegacyFalloutProperty Include="NukeScriptDirectory" Condition="'$(NukeScriptDirectory)' != ''" >FalloutScriptDirectory + <_LegacyFalloutProperty Include="NukeTelemetryVersion" Condition="'$(NukeTelemetryVersion)' != ''" >FalloutTelemetryVersion + <_LegacyFalloutProperty Include="NukeDefaultExcludes" Condition="'$(NukeDefaultExcludes)' != ''" >FalloutDefaultExcludes + <_LegacyFalloutProperty Include="NukeExcludeBoot" Condition="'$(NukeExcludeBoot)' != ''" >FalloutExcludeBoot + <_LegacyFalloutProperty Include="NukeExcludeConfig" Condition="'$(NukeExcludeConfig)' != ''" >FalloutExcludeConfig + <_LegacyFalloutProperty Include="NukeExcludeLogs" Condition="'$(NukeExcludeLogs)' != ''" >FalloutExcludeLogs + <_LegacyFalloutProperty Include="NukeExcludeDirectoryBuild" Condition="'$(NukeExcludeDirectoryBuild)' != ''" >FalloutExcludeDirectoryBuild + <_LegacyFalloutProperty Include="NukeExcludeCi" Condition="'$(NukeExcludeCi)' != ''" >FalloutExcludeCi + <_LegacyFalloutProperty Include="NukeContinueOnError" Condition="'$(NukeContinueOnError)' != ''" >FalloutContinueOnError + <_LegacyFalloutProperty Include="NukeTaskTimeout" Condition="'$(NukeTaskTimeout)' != ''" >FalloutTaskTimeout + <_LegacyFalloutProperty Include="NukeTimeout" Condition="'$(NukeTimeout)' != ''" >FalloutTaskTimeout + <_LegacyFalloutProperty Include="NukeBaseDirectory" Condition="'$(NukeBaseDirectory)' != ''" >FalloutBaseDirectory + <_LegacyFalloutProperty Include="NukeBaseNamespace" Condition="'$(NukeBaseNamespace)' != ''" >FalloutBaseNamespace + <_LegacyFalloutProperty Include="NukeUseNestedNamespaces" Condition="'$(NukeUseNestedNamespaces)' != ''" >FalloutUseNestedNamespaces + <_LegacyFalloutProperty Include="NukeRepositoryUrl" Condition="'$(NukeRepositoryUrl)' != ''" >FalloutRepositoryUrl + <_LegacyFalloutProperty Include="NukeUpdateReferences" Condition="'$(NukeUpdateReferences)' != ''" >FalloutUpdateReferences + <_LegacyFalloutProperty Include="NukeSpecificationFiles" Condition="'@(NukeSpecificationFiles)' != ''" >FalloutSpecificationFiles + <_LegacyFalloutProperty Include="NukeExternalFiles" Condition="'@(NukeExternalFiles)' != ''" >FalloutExternalFiles + + + + + + diff --git a/src/Fallout.GlobalTool/templates/_build.csproj b/src/Fallout.GlobalTool/templates/_build.csproj index b8b34ddcc..54b6ef9ab 100644 --- a/src/Fallout.GlobalTool/templates/_build.csproj +++ b/src/Fallout.GlobalTool/templates/_build.csproj @@ -5,9 +5,9 @@ _TARGET_FRAMEWORK_ CS0649;CS0169;CA1050;CA1822;CA2211;IDE1006 - _ROOT_DIRECTORY_ - _SCRIPT_DIRECTORY_ - _TELEMETRY_VERSION_ + _ROOT_DIRECTORY_ + _SCRIPT_DIRECTORY_ + _TELEMETRY_VERSION_ false diff --git a/src/Fallout.MSBuildTasks/Fallout.MSBuildTasks.targets b/src/Fallout.MSBuildTasks/Fallout.MSBuildTasks.targets index 85d46b220..7a73c6c9c 100644 --- a/src/Fallout.MSBuildTasks/Fallout.MSBuildTasks.targets +++ b/src/Fallout.MSBuildTasks/Fallout.MSBuildTasks.targets @@ -1,29 +1,59 @@ - + - - $(MSBuildThisFileDirectory)\$(MSBuildThisFileName).dll - - - - - + - False - 5000 + $(NukeTasksAssembly) + $(MSBuildThisFileDirectory)\$(MSBuildThisFileName).dll + + $(NukeContinueOnError) + False + + $(NukeTaskTimeout) + $(NukeTimeout) + 5000 + + $(NukeBaseDirectory) + $(MSBuildProjectDirectory) + + $(NukeUseNestedNamespaces) + False - $(MSBuildProjectDirectory) - False - $(RepositoryUrl) - True + $(NukeRepositoryUrl) + $(RepositoryUrl) + + $(NukeUpdateReferences) + True + + $(NukeBaseNamespace) - + + + + + + + + + + + + + + - + - + @@ -46,13 +76,22 @@ - + + SpecificationFiles="@(FalloutSpecificationFiles)" + BaseDirectory="$(FalloutBaseDirectory)" + UseNestedNamespaces="$(FalloutUseNestedNamespaces)" + BaseNamespace="$(FalloutBaseNamespace)" + UpdateReferences="$(FalloutUpdateReferences)"/> + + + + + + From 88c5b1658cc16684af552b76b4910f63ab5c8a39 Mon Sep 17 00:00:00 2001 From: Chrison Simtian Date: Thu, 21 May 2026 12:54:56 +1200 Subject: [PATCH 2/3] chore(rebrand): deprecate NUKE_* env vars with FALLOUT002 stderr warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renames the four legacy env var keys to FALLOUT_*, with readers falling back to NUKE_* if only the legacy is set. Each fallback path emits a one-time FALLOUT002 warning to stderr. NUKE_TELEMETRY_OPTOUT → FALLOUT_TELEMETRY_OPTOUT NUKE_GLOBAL_TOOL_VERSION → FALLOUT_GLOBAL_TOOL_VERSION NUKE_GLOBAL_TOOL_START_TIME → FALLOUT_GLOBAL_TOOL_START_TIME NUKE_INTERNAL_INTERCEPTOR → FALLOUT_INTERNAL_INTERCEPTOR ## Mechanism New helper Fallout.Common.LegacyEnvironment (in Fallout.Build.Shared) reads the preferred name, falls back to the legacy with a one-time stderr warning, and otherwise returns null. Used by: - FalloutBuild.IsInterceptorExecution - Telemetry.Properties global tool version + start time reads Telemetry opt-out uses ParameterService.GetParameter with a manual fallback chain (no warning — opt-out check runs before logging is up and warning a user who's actively trying to opt out feels backwards). ## Symbol stability The Telemetry.OptOutEnvironmentKey public constant keeps its name but its value flips from "NUKE_TELEMETRY_OPTOUT" to "FALLOUT_TELEMETRY_OPTOUT". Any caller using the symbol gets the new name automatically. Added Telemetry.LegacyOptOutEnvironmentKey for the legacy. Constants.{GlobalToolVersion,GlobalToolStartTime,Interceptor}EnvironmentKey ditto — symbol stable, value updated, paired Legacy* constants added. ## Repo-side updates - build.sh, build.ps1, build/Build.cs: emit FALLOUT_TELEMETRY_OPTOUT now - CLAUDE.md telemetry convention note updated - docs/01-getting-started/07-telemetry.md notes both names - tests/Fallout.Build.Tests/ExecutionTestsInitializer.cs unchanged — it uses the OptOutEnvironmentKey symbol which now resolves to the new value ## Verification - dotnet build fallout.slnx -c Debug: 0 errors, 14 pre-existing warnings - dotnet test fallout.slnx: 391 passed, 7 skipped, 0 failed Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 2 +- build.ps1 | 2 +- build.sh | 2 +- build/Build.cs | 2 +- docs/01-getting-started/07-telemetry.md | 2 +- src/Fallout.Build.Shared/Constants.cs | 12 +++- src/Fallout.Build.Shared/LegacyEnvironment.cs | 66 +++++++++++++++++++ src/Fallout.Build/FalloutBuild.cs | 2 +- .../Telemetry/Telemetry.Properties.cs | 14 ++-- src/Fallout.Build/Telemetry/Telemetry.cs | 7 +- 10 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 src/Fallout.Build.Shared/LegacyEnvironment.cs diff --git a/CLAUDE.md b/CLAUDE.md index 5baad3431..e64f3a838 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -80,7 +80,7 @@ Validation workflows: **`ubuntu-latest`** runs on every PR targeting `main` (wit - **Tool wrappers**: copy/paste from neighbours; cover full commands; use ``, ``, `
    `/`
      `, ``, `` in `help`; don't write `secret: false` or `default: xxx`. - **Tests next to code, separate folder**: every `Foo` project under `src/` has a sibling `Foo.Tests` project under `tests/`. Mirror the namespace. - **No IDE-specific style files committed.** `.editorconfig` and `*.DotSettings` were removed during the takeover — relying on `dotnet format` defaults and review. -- **Telemetry opt-out is set in test runs** (`NUKE_TELEMETRY_OPTOUT=true`). Keep it that way. +- **Telemetry opt-out is set in test runs** (`FALLOUT_TELEMETRY_OPTOUT=true`). Keep it that way. - **License header**: every source file starts with the 4-line `// Copyright … Maintainers of Fallout. // Originally based on NUKE …` block — copy from a neighbouring file when adding new ones. ## What not to do diff --git a/build.ps1 b/build.ps1 index f6dd8d5f3..32cd6474c 100644 --- a/build.ps1 +++ b/build.ps1 @@ -23,7 +23,7 @@ $DotNetChannel = "STS" $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 $env:DOTNET_NOLOGO = 1 $env:DOTNET_ROLL_FORWARD = "Major" -$env:NUKE_TELEMETRY_OPTOUT = 1 +$env:FALLOUT_TELEMETRY_OPTOUT = 1 ########################################################################### # EXECUTION diff --git a/build.sh b/build.sh index d2ac03df8..518e0215e 100755 --- a/build.sh +++ b/build.sh @@ -19,7 +19,7 @@ DOTNET_CHANNEL="STS" export DOTNET_CLI_TELEMETRY_OPTOUT=1 export DOTNET_NOLOGO=1 export DOTNET_ROLL_FORWARD="Major" -export NUKE_TELEMETRY_OPTOUT=1 +export FALLOUT_TELEMETRY_OPTOUT=1 ########################################################################### # EXECUTION diff --git a/build/Build.cs b/build/Build.cs index ae6084535..5e4630d9a 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -92,7 +92,7 @@ from framework in project.GetTargetFrameworks() public int TestDegreeOfParallelism { get; } = 1; Configure ITest.TestSettings => _ => _ - .SetProcessEnvironmentVariable("NUKE_TELEMETRY_OPTOUT", bool.TrueString); + .SetProcessEnvironmentVariable("FALLOUT_TELEMETRY_OPTOUT", bool.TrueString); Target ITest.Test => _ => _ .Inherit() diff --git a/docs/01-getting-started/07-telemetry.md b/docs/01-getting-started/07-telemetry.md index e228e7cc0..6310ab779 100644 --- a/docs/01-getting-started/07-telemetry.md +++ b/docs/01-getting-started/07-telemetry.md @@ -81,4 +81,4 @@ Whenever a type does not originate from the `Nuke` namespace, it is replaced wit ## How to opt out -The telemetry feature is enabled by default. To opt out, set the `NUKE_TELEMETRY_OPTOUT` environment variable to `1` or `true`. +The telemetry feature is enabled by default. To opt out, set the `FALLOUT_TELEMETRY_OPTOUT` environment variable to `1` or `true`. The legacy `NUKE_TELEMETRY_OPTOUT` name is still honoured during the 10.x line for backwards compatibility, but will be removed in 11.0. diff --git a/src/Fallout.Build.Shared/Constants.cs b/src/Fallout.Build.Shared/Constants.cs index f882f36e2..fb6b4e896 100644 --- a/src/Fallout.Build.Shared/Constants.cs +++ b/src/Fallout.Build.Shared/Constants.cs @@ -41,9 +41,15 @@ internal static class Constants internal const string ParametersFilePrefix = "parameters"; internal const string DefaultProfileName = "$default"; - internal const string GlobalToolVersionEnvironmentKey = "NUKE_GLOBAL_TOOL_VERSION"; - internal const string GlobalToolStartTimeEnvironmentKey = "NUKE_GLOBAL_TOOL_START_TIME"; - internal const string InterceptorEnvironmentKey = "NUKE_INTERNAL_INTERCEPTOR"; + internal const string GlobalToolVersionEnvironmentKey = "FALLOUT_GLOBAL_TOOL_VERSION"; + internal const string GlobalToolStartTimeEnvironmentKey = "FALLOUT_GLOBAL_TOOL_START_TIME"; + internal const string InterceptorEnvironmentKey = "FALLOUT_INTERNAL_INTERCEPTOR"; + + // Legacy NUKE_* env var names — readers fall back to these via LegacyEnvironment.Read. + // Writers (e.g. global tool spawning the build) only emit the FALLOUT_* form above. + internal const string LegacyGlobalToolVersionEnvironmentKey = "NUKE_GLOBAL_TOOL_VERSION"; + internal const string LegacyGlobalToolStartTimeEnvironmentKey = "NUKE_GLOBAL_TOOL_START_TIME"; + internal const string LegacyInterceptorEnvironmentKey = "NUKE_INTERNAL_INTERCEPTOR"; // Canonical project URLs. Until P7 (domain registration) lands, these all point at the GitHub fork. // To migrate to fallout., edit FalloutWebsite / FalloutRepository here — call sites already use the constants. diff --git a/src/Fallout.Build.Shared/LegacyEnvironment.cs b/src/Fallout.Build.Shared/LegacyEnvironment.cs new file mode 100644 index 000000000..6d92e29c0 --- /dev/null +++ b/src/Fallout.Build.Shared/LegacyEnvironment.cs @@ -0,0 +1,66 @@ +// Copyright 2026 Maintainers of Fallout. +// Originally based on NUKE by Matthias Koch and contributors. +// Distributed under the MIT License. +// https://github.com/ChrisonSimtian/Fallout/blob/main/LICENSE + +using System; +using System.Collections.Generic; + +namespace Fallout.Common; + +/// +/// Helpers for reading environment variables during the NUKE → Fallout rename. +/// Reads the preferred (FALLOUT_*) name first, falls back to the legacy (NUKE_*) +/// name if only the legacy is set, and emits a one-time warning per legacy key. +/// +internal static class LegacyEnvironment +{ + private static readonly HashSet WarnedLegacyKeys = new(StringComparer.Ordinal); + private static readonly object WarnedLegacyKeysLock = new(); + + /// + /// Returns the value of if set; otherwise the value of + /// (with a deprecation warning); otherwise null. + /// + public static string Read(string preferredName, string legacyName) + { + var preferred = Environment.GetEnvironmentVariable(preferredName); + if (!string.IsNullOrEmpty(preferred)) + return preferred; + + var legacy = Environment.GetEnvironmentVariable(legacyName); + if (string.IsNullOrEmpty(legacy)) + return null; + + WarnOnce(legacyName, preferredName); + return legacy; + } + + /// + /// Same as but for the dictionary-style access used by EnvironmentInfo.Variables. + /// + public static string ReadFromVariables(IReadOnlyDictionary variables, string preferredName, string legacyName) + { + if (variables.TryGetValue(preferredName, out var preferred) && !string.IsNullOrEmpty(preferred)) + return preferred; + + if (!variables.TryGetValue(legacyName, out var legacy) || string.IsNullOrEmpty(legacy)) + return null; + + WarnOnce(legacyName, preferredName); + return legacy; + } + + private static void WarnOnce(string legacyName, string preferredName) + { + lock (WarnedLegacyKeysLock) + { + if (!WarnedLegacyKeys.Add(legacyName)) + return; + } + + Console.Error.WriteLine( + $"warning FALLOUT002: Environment variable '{legacyName}' is deprecated; rename to '{preferredName}'. " + + "The legacy name still works in 10.x but will be removed in 11.0."); + } +} diff --git a/src/Fallout.Build/FalloutBuild.cs b/src/Fallout.Build/FalloutBuild.cs index e676b2335..bae96c1a4 100644 --- a/src/Fallout.Build/FalloutBuild.cs +++ b/src/Fallout.Build/FalloutBuild.cs @@ -201,7 +201,7 @@ ExecutionStatus.Aborted or /// public int? ExitCode { get; set; } - private bool IsInterceptorExecution => Environment.GetEnvironmentVariable(InterceptorEnvironmentKey) == "1"; + private bool IsInterceptorExecution => LegacyEnvironment.Read(InterceptorEnvironmentKey, LegacyInterceptorEnvironmentKey) == "1"; public void ReportSummary(Configure> configurator = null) { diff --git a/src/Fallout.Build/Telemetry/Telemetry.Properties.cs b/src/Fallout.Build/Telemetry/Telemetry.Properties.cs index bda066254..2e0fbe2c3 100644 --- a/src/Fallout.Build/Telemetry/Telemetry.Properties.cs +++ b/src/Fallout.Build/Telemetry/Telemetry.Properties.cs @@ -37,9 +37,12 @@ private static Dictionary GetCommonProperties(IFalloutBuild buil ["os_platform"] = EnvironmentInfo.Platform.ToString(), ["os_architecture"] = RuntimeInformation.OSArchitecture.ToString(), ["version_dotnet_sdk"] = version, - ["version_nuke_common"] = build != null ? typeof(FalloutBuild).Assembly.GetVersionText() : null, - ["version_nuke_global_tool"] = build != null - ? EnvironmentInfo.Variables.GetValueOrDefault(Constants.GlobalToolVersionEnvironmentKey) + ["version_fallout_common"] = build != null ? typeof(FalloutBuild).Assembly.GetVersionText() : null, + ["version_fallout_global_tool"] = build != null + ? LegacyEnvironment.ReadFromVariables( + EnvironmentInfo.Variables, + Constants.GlobalToolVersionEnvironmentKey, + Constants.LegacyGlobalToolVersionEnvironmentKey) : Assembly.GetEntryAssembly().GetVersionText() }; } @@ -80,7 +83,10 @@ private static Dictionary GetRepositoryProperties(string directo private static ReadOnlyDictionary GetBuildProperties(IFalloutBuild build) { - var startTimeString = EnvironmentInfo.Variables.GetValueOrDefault(Constants.GlobalToolStartTimeEnvironmentKey); + var startTimeString = LegacyEnvironment.ReadFromVariables( + EnvironmentInfo.Variables, + Constants.GlobalToolStartTimeEnvironmentKey, + Constants.LegacyGlobalToolStartTimeEnvironmentKey); var compileTime = startTimeString != null ? DateTime.Now.Subtract(DateTime.Parse(startTimeString)) : default(TimeSpan?); diff --git a/src/Fallout.Build/Telemetry/Telemetry.cs b/src/Fallout.Build/Telemetry/Telemetry.cs index 1a8ebb9f1..4983849ec 100644 --- a/src/Fallout.Build/Telemetry/Telemetry.cs +++ b/src/Fallout.Build/Telemetry/Telemetry.cs @@ -23,7 +23,8 @@ internal static partial class Telemetry // https://docs.microsoft.com/en-us/azure/azure-monitor/app/console // https://docs.microsoft.com/en-us/azure/azure-monitor/app/ip-collection - public const string OptOutEnvironmentKey = "NUKE_TELEMETRY_OPTOUT"; + public const string OptOutEnvironmentKey = "FALLOUT_TELEMETRY_OPTOUT"; + public const string LegacyOptOutEnvironmentKey = "NUKE_TELEMETRY_OPTOUT"; public const int CurrentVersion = 1; // Telemetry is currently a no-op for Fallout: the previous InstrumentationKey routed data @@ -38,7 +39,9 @@ internal static partial class Telemetry static Telemetry() { - var optoutParameter = ParameterService.GetParameter(OptOutEnvironmentKey) ?? string.Empty; + var optoutParameter = ParameterService.GetParameter(OptOutEnvironmentKey) + ?? ParameterService.GetParameter(LegacyOptOutEnvironmentKey) + ?? string.Empty; if (optoutParameter == "1" || optoutParameter.EqualsOrdinalIgnoreCase(bool.TrueString)) return; From 35dcc8e152918f0ccde1b396efd410b72bfdddbe Mon Sep 17 00:00:00 2001 From: Chrison Simtian Date: Thu, 21 May 2026 12:59:11 +1200 Subject: [PATCH 3/3] chore(rebrand): migrate credential store + telemetry version property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Credential store `Constants.GetCredentialStoreName` now returns the canonical `"Fallout: {root} ({profile})"` keychain entry name. Added `GetLegacyCredentialStoreName` returning the legacy `"NUKE: ..."` form. Readers (`CredentialStore.GetPassword` for build-time secret lookup, `Program.Secrets` for the global tool's `dotnet fallout :secrets` command) try the canonical entry first, fall back to legacy on miss: - Build-time path: prints FALLOUT003 stderr warning + uses legacy password for that run; user must re-run `:secrets` to migrate. - :secrets command path: prints a Host.Warning, opportunistically re-saves the legacy password to the canonical entry so subsequent loads hit the canonical key directly. Writers (`CredentialStore.SavePassword`, `DeletePassword`) always emit the canonical form. Legacy entry is never written to or deleted automatically — left intact so 10.x can still pick it up on miss. ## Telemetry version property `Telemetry.VersionPropertyName` flips from `"NukeTelemetryVersion"` to `"FalloutTelemetryVersion"`. Added `LegacyVersionPropertyName` for the legacy lookup — `ConfirmVersion` checks the canonical name first, falls back to legacy if the user's `_build.csproj` still uses the old name. This pairs with the MSBuild prop deprecation earlier in this branch: the user's `_build.csproj` can have either property set; the Fallout* form is preferred but Nuke* keeps working. ## Verification - dotnet build fallout.slnx -c Debug: 0 errors, 14 pre-existing warnings - dotnet test fallout.slnx: 391 passed, 7 skipped, 0 failed Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Fallout.Build.Shared/Constants.cs | 7 +++++++ src/Fallout.Build/Telemetry/Telemetry.cs | 6 ++++-- .../Utilities/CredentialStore.cs | 17 +++++++++++++++++ src/Fallout.GlobalTool/Program.Secrets.cs | 19 +++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/Fallout.Build.Shared/Constants.cs b/src/Fallout.Build.Shared/Constants.cs index fb6b4e896..983e793dc 100644 --- a/src/Fallout.Build.Shared/Constants.cs +++ b/src/Fallout.Build.Shared/Constants.cs @@ -161,6 +161,13 @@ public static IEnumerable GetProfileNames(AbsolutePath rootDirectory) } internal static string GetCredentialStoreName(AbsolutePath rootDirectory, [CanBeNull] string profile) + { + return $"Fallout: {rootDirectory} ({profile ?? DefaultProfileName})"; + } + + // Pre-rename name. Readers fall back to this when the canonical entry is missing. + // Writers (SavePassword / Secrets command) only emit the canonical form above. + internal static string GetLegacyCredentialStoreName(AbsolutePath rootDirectory, [CanBeNull] string profile) { return $"NUKE: {rootDirectory} ({profile ?? DefaultProfileName})"; } diff --git a/src/Fallout.Build/Telemetry/Telemetry.cs b/src/Fallout.Build/Telemetry/Telemetry.cs index 4983849ec..11b30afb9 100644 --- a/src/Fallout.Build/Telemetry/Telemetry.cs +++ b/src/Fallout.Build/Telemetry/Telemetry.cs @@ -32,7 +32,8 @@ internal static partial class Telemetry // populate. When/if we stand up a Fallout-controlled endpoint, fill in the key here. // Original NUKE key (do NOT reuse): "4b987be9-f807-4846-b777-4291f3a5ad8b" private const string InstrumentationKey = ""; - private const string VersionPropertyName = "NukeTelemetryVersion"; + private const string VersionPropertyName = "FalloutTelemetryVersion"; + private const string LegacyVersionPropertyName = "NukeTelemetryVersion"; private static readonly TelemetryClient s_client; private static readonly int? s_confirmedVersion; @@ -83,7 +84,8 @@ AbsolutePath GetCookieFile(string name, int version) } var project = ProjectModelTasks.ParseProject(FalloutBuild.BuildProjectFile); - var property = project.Properties.SingleOrDefault(x => x.Name.EqualsOrdinalIgnoreCase(VersionPropertyName)); + var property = project.Properties.SingleOrDefault(x => x.Name.EqualsOrdinalIgnoreCase(VersionPropertyName)) + ?? project.Properties.SingleOrDefault(x => x.Name.EqualsOrdinalIgnoreCase(LegacyVersionPropertyName)); if (property?.EvaluatedValue != CurrentVersion.ToString()) { if (FalloutBuild.IsServerBuild) diff --git a/src/Fallout.Build/Utilities/CredentialStore.cs b/src/Fallout.Build/Utilities/CredentialStore.cs index bd4aff1ec..f1ab28d96 100644 --- a/src/Fallout.Build/Utilities/CredentialStore.cs +++ b/src/Fallout.Build/Utilities/CredentialStore.cs @@ -75,12 +75,29 @@ string PromptForPassword() } var credentialStoreName = Constants.GetCredentialStoreName(rootDirectory, profile); + var legacyCredentialStoreName = Constants.GetLegacyCredentialStoreName(rootDirectory, profile); var passwordParameterName = Constants.GetProfilePasswordParameterName(profile); + return TryGetPassword(credentialStoreName) ?? + TryGetLegacyPasswordWithWarning(legacyCredentialStoreName, credentialStoreName) ?? ParameterService.GetParameter(passwordParameterName) ?? PromptForPassword(); } + [CanBeNull] + private static string TryGetLegacyPasswordWithWarning(string legacyName, string newName) + { + var password = TryGetPassword(legacyName); + if (password == null) + return null; + + Console.Error.WriteLine( + $"warning FALLOUT003: Found credentials under legacy keychain entry '{legacyName}'. " + + $"Falling back to legacy entry for this run. Re-run `dotnet fallout :secrets` to migrate to '{newName}'. " + + "The legacy entry will no longer be read in 11.0."); + return password; + } + public static string CreateNewPassword(out bool generated) { while (true) diff --git a/src/Fallout.GlobalTool/Program.Secrets.cs b/src/Fallout.GlobalTool/Program.Secrets.cs index a67436831..a2f19ed64 100644 --- a/src/Fallout.GlobalTool/Program.Secrets.cs +++ b/src/Fallout.GlobalTool/Program.Secrets.cs @@ -48,7 +48,21 @@ public static int Secrets(string[] args, [CanBeNull] AbsolutePath rootDirectory, var generatedPassword = false; var credentialStoreName = GetCredentialStoreName(rootDirectory, profile); + var legacyCredentialStoreName = GetLegacyCredentialStoreName(rootDirectory, profile); var password = CredentialStore.TryGetPassword(credentialStoreName); + var fromLegacyCredentialStore = false; + if (password == null) + { + password = CredentialStore.TryGetPassword(legacyCredentialStoreName); + fromLegacyCredentialStore = password != null; + if (fromLegacyCredentialStore) + { + Host.Warning( + $"Found credentials under legacy keychain entry '{legacyCredentialStoreName}'. " + + $"Migrating to '{credentialStoreName}'. The legacy entry will no longer be read in 11.0."); + } + } + var fromCredentialStore = password != null; password ??= CredentialStore.CreateNewPassword(out generatedPassword); var existingSecrets = LoadSecrets(secretParameters, password, parametersFile); @@ -58,6 +72,11 @@ public static int Secrets(string[] args, [CanBeNull] AbsolutePath rootDirectory, if (generatedPassword || PromptForConfirmation($"Save password to keychain? (associated with '{rootDirectory}')")) CredentialStore.SavePassword(credentialStoreName, password); } + else if (fromLegacyCredentialStore) + { + // Forward the legacy password to the canonical entry so future loads find it directly. + CredentialStore.SavePassword(credentialStoreName, password); + } var options = secretParameters .Concat(SaveAndExit, DiscardAndExit)