From 404688ebd07852301813f3c4c2f17f20ef3ae843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zeynep=20Gizem=20F=C4=B1rat?= Date: Thu, 10 Sep 2026 10:38:52 +0300 Subject: [PATCH 1/5] Add CPM include support to switch-to-nightly Extend `switch-to-nightly` with `--include` and `--exclude-packages` options to optionally update Central Package Management files (like `Directory.Packages.props`) alongside regular project/package updates. The updater now resolves the nightly ABP version once, updates `PackageVersion Include="Volo.*"` entries with exclusion and LeptonX/Studio safeguards, and logs/continues on MyGet or file-write failures. Also fixes `.npmrc` registry appending to insert a newline before the added registry entry. --- .../Cli/Commands/SwitchToNightlyCommand.cs | 2 + .../ProjectModification/NpmPackagesUpdater.cs | 2 +- .../PackagePreviewSwitcher.cs | 95 ++++++++++++++-- .../VoloNugetPackagesVersionUpdater.cs | 103 +++++++++++++++++- 4 files changed, 191 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SwitchToNightlyCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SwitchToNightlyCommand.cs index b7dca40bad..3f1651eb77 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SwitchToNightlyCommand.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/SwitchToNightlyCommand.cs @@ -32,6 +32,8 @@ public class SwitchToNightlyCommand : IConsoleCommand, ITransientDependency sb.AppendLine(""); sb.AppendLine("Options:"); sb.AppendLine("-d|--directory"); + sb.AppendLine("-i|--include (optional) comma-separated list of Directory.Packages.props-style files to also update for Central Package Management"); + sb.AppendLine("-ep|--exclude-packages (optional) comma-separated list of package ids to never touch in --include files"); sb.AppendLine(""); sb.AppendLine("See the documentation for more info: https://abp.io/docs/latest/cli"); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs index 9b7340fdd7..379fc45bfa 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/NpmPackagesUpdater.cs @@ -132,7 +132,7 @@ public class NpmPackagesUpdater : ITransientDependency if (!fileContent.Contains(volosoftRegistry)) { - fileContent += volosoftRegistry; + fileContent += Environment.NewLine + volosoftRegistry; } File.WriteAllText(fileName, fileContent); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs index 13046dae78..6addbd8bb8 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -66,13 +67,13 @@ public class PackagePreviewSwitcher : ITransientDependency if (solutionPaths.Any()) { - await SwitchSolutionsToNightlyPreview(solutionPaths); + await SwitchSolutionsToNightlyPreview(solutionPaths, commandLineArgs); } else { var projectPaths = GetProjectPaths(commandLineArgs); - - await SwitchProjectsToNightlyPreview(projectPaths); + + await SwitchProjectsToNightlyPreview(projectPaths, commandLineArgs); } } @@ -185,13 +186,16 @@ public class PackagePreviewSwitcher : ITransientDependency } } - private async Task SwitchProjectsToNightlyPreview(List projects) + private async Task SwitchProjectsToNightlyPreview(List projects, CommandLineArgs commandLineArgs) { + var (includeFiles, excludedPackages, latestVersionFromMyGet) = await ResolveNightlyIncludeContextAsync(commandLineArgs); + foreach (var project in projects) { var folder = Path.GetDirectoryName(project); + var projectFolder = FindSolutionFolder(project) ?? folder; - _packageSourceManager.Add(FindSolutionFolder(project) ?? folder, "ABP Nightly", + _packageSourceManager.Add(projectFolder, "ABP Nightly", "https://www.myget.org/F/abp-nightly/api/v3/index.json", "Volo.*"); await _nugetPackagesVersionUpdater.UpdateSolutionAsync( @@ -201,11 +205,17 @@ public class PackagePreviewSwitcher : ITransientDependency await _npmPackagesUpdater.Update( folder, true); + + // See SwitchSolutionsToNightlyPreview for the race-avoidance rationale: this + // sequential pass always runs after the per-project UpdateSolutionAsync above. + await UpdateIncludedCentralPackageFilesAsync(includeFiles, excludedPackages, latestVersionFromMyGet, projectFolder); } } - private async Task SwitchSolutionsToNightlyPreview(List solutionPaths) + private async Task SwitchSolutionsToNightlyPreview(List solutionPaths, CommandLineArgs commandLineArgs) { + var (includeFiles, excludedPackages, latestVersionFromMyGet) = await ResolveNightlyIncludeContextAsync(commandLineArgs); + foreach (var solutionPath in solutionPaths) { var solutionFolder = Path.GetDirectoryName(solutionPath); @@ -232,6 +242,59 @@ public class PackagePreviewSwitcher : ITransientDependency solutionAngularFolder, true); } + + // Optional Central Package Management support: only runs when --include is + // explicitly passed, and only after UpdateSolutionAsync's internal parallel + // (Task.WaitAll) per-project update has fully completed, so no --include file + // is ever touched concurrently with anything else. + await UpdateIncludedCentralPackageFilesAsync(includeFiles, excludedPackages, latestVersionFromMyGet, solutionFolder); + } + } + + private async Task<(List IncludeFiles, List ExcludedPackages, string LatestVersionFromMyGet)> ResolveNightlyIncludeContextAsync( + CommandLineArgs commandLineArgs) + { + var includeFiles = GetCommaSeparatedOption(commandLineArgs, Options.Include.Short, Options.Include.Long); + var excludedPackages = GetCommaSeparatedOption(commandLineArgs, Options.Exclude.Short, Options.Exclude.Long); + + if (!includeFiles.Any()) + { + return (includeFiles, excludedPackages, null); + } + + string latestVersionFromMyGet; + try + { + latestVersionFromMyGet = await _nugetPackagesVersionUpdater.GetLatestVersionFromMyGet("Volo.Abp.Core"); + } + catch (Exception ex) + { + // Don't let a transient MyGet failure abort the whole switch-to-nightly run + // (source registration / regular PackageReference updates below must still + // proceed for every solution/project) - just skip the --include pass. + Logger.LogWarning(ex, "Could not resolve the latest Volo.Abp.Core nightly version; --include files will be skipped for this run."); + latestVersionFromMyGet = null; + } + + return (includeFiles, excludedPackages, latestVersionFromMyGet); + } + + private async Task UpdateIncludedCentralPackageFilesAsync( + List includeFiles, + List excludedPackages, + string latestVersionFromMyGet, + string baseFolder) + { + foreach (var includeFile in includeFiles) + { + var resolvedPath = Path.IsPathRooted(includeFile) + ? includeFile + : Path.Combine(baseFolder, includeFile); + + await _nugetPackagesVersionUpdater.UpdateCentralPackageVersionsAsync( + resolvedPath, + latestVersionFromMyGet, + excludedPackages); } } @@ -285,6 +348,14 @@ public class PackagePreviewSwitcher : ITransientDependency ?? Directory.GetCurrentDirectory(); } + private List GetCommaSeparatedOption(CommandLineArgs commandLineArgs, string shortName, string longName) + { + var raw = commandLineArgs.Options.GetOrNull(shortName, longName); + return raw.IsNullOrWhiteSpace() + ? new List() + : raw.Split(',').Select(s => s.Trim()).Where(s => !s.IsNullOrWhiteSpace()).ToList(); + } + private string GetSolutionAngularFolder(string solutionFolder) { var upperAngularPath = Path.Combine(Directory.GetParent(solutionFolder)?.FullName ?? "", "angular"); @@ -340,5 +411,15 @@ public class PackagePreviewSwitcher : ITransientDependency public const string Short = "d"; public const string Long = "directory"; } + public static class Include + { + public const string Short = "i"; + public const string Long = "include"; + } + public static class Exclude + { + public const string Short = "ep"; + public const string Long = "exclude-packages"; + } } } diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index 924d6041b1..6500674799 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -217,8 +217,8 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency } var currentVersion = versionAttribute.Value; - var isLeptonXPackage = packageId.Contains("LeptonX"); - var isStudioPackage = packageId.StartsWith("Volo.Abp.Studio."); + var isLeptonXPackage = IsLeptonXPackage(packageId); + var isStudioPackage = IsStudioPackage(packageId); if(isLeptonXPackage) { //'SemanticVersion.TryParse' can not parse the version if the version contains floating version resolution, such as '*-*' @@ -366,10 +366,107 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency return await Task.FromResult(content); } - private async Task GetLatestVersionFromMyGet(string packageId) + private static bool IsLeptonXPackage(string packageId) => packageId.Contains("LeptonX"); + + private static bool IsStudioPackage(string packageId) => packageId.StartsWith("Volo.Abp.Studio."); + + internal async Task GetLatestVersionFromMyGet(string packageId) { var myGetPack = await _myGetPackageListFinder.GetPackagesAsync(); return myGetPack.Packages.FirstOrDefault(p => p.Id == packageId)?.Versions.LastOrDefault(); } + + /// + /// Updates <PackageVersion Include="Volo.*"> entries in a Central Package Management + /// props file (e.g. Directory.Packages.props) to . + /// Regular PackageReference-based updates (UpdateSolutionAsync/UpdateProjectAsync) already + /// skip any PackageReference with no Version attribute (i.e. CPM-managed packages) - this + /// method is the explicit, opt-in counterpart for callers that also want those central + /// versions kept in sync. Not invoked unless a caller (e.g. the switch-to-nightly --include + /// option) explicitly requests it. + /// + public async Task UpdateCentralPackageVersionsAsync( + string filePath, + string latestVersionFromMyGet, + IEnumerable excludedPackageIds = null) + { + if (!File.Exists(filePath)) + { + Logger.LogWarning("--include file not found, skipped: {FilePath}", filePath); + return; + } + + if (latestVersionFromMyGet == null) + { + return; + } + + var excluded = new HashSet(excludedPackageIds ?? Enumerable.Empty(), StringComparer.OrdinalIgnoreCase); + + try + { + using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) + { + using (var sr = new StreamReader(fs, Encoding.Default, true)) + { + var fileContent = await sr.ReadToEndAsync(); + + var doc = new XmlDocument { PreserveWhitespace = true }; + doc.LoadXml(fileContent); + + var packageNodeList = doc.SelectNodes("//PackageVersion[starts-with(@Include, 'Volo.')]"); + if (packageNodeList != null) + { + foreach (XmlNode package in packageNodeList) + { + var packageId = package.Attributes?["Include"]?.Value; + if (packageId == null || excluded.Contains(packageId)) + { + continue; + } + + // LeptonX and Studio packages follow their own, independent version + // stream (see IsLeptonXPackage/IsStudioPackage, also used by + // UpdateVoloPackagesAsync above) - never stamp them with the + // Volo.Abp.Core anchor version, regardless of --exclude-packages. + if (IsLeptonXPackage(packageId) || IsStudioPackage(packageId)) + { + continue; + } + + var versionAttribute = package.Attributes["Version"]; + if (versionAttribute == null) + { + continue; + } + + if (versionAttribute.Value != latestVersionFromMyGet) + { + Logger.LogInformation("Updating central package \"{PackageId}\" from v{CurrentVersion} to v{LatestVersion}", packageId, versionAttribute.Value, latestVersionFromMyGet); + versionAttribute.Value = latestVersionFromMyGet; + } + } + } + + fs.Seek(0, SeekOrigin.Begin); + fs.SetLength(0); + + using (var sw = new StreamWriter(fs, DefaultEncoding)) + { + await sw.WriteAsync(doc.OuterXml); + await sw.FlushAsync(); + } + } + } + } + catch (Exception ex) + { + // The file is truncated before the updated XML is written back, so a failure here + // (disk full, process killed, file locked mid-write) can leave it empty/partially + // written on disk. Logged as an error (not a warning) so this isn't missed - the + // rest of the switch-to-nightly run still continues for other solutions/files. + Logger.LogError(ex, "Failed to update central package versions in \"{FilePath}\". The file may now be empty or partially written - please check it manually.", filePath); + } + } } From aaee0c922813eff87a22bb5a1bb79b481b9157be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zeynep=20Gizem=20F=C4=B1rat?= Date: Thu, 10 Sep 2026 10:41:54 +0300 Subject: [PATCH 2/5] Handle missing nightly version for --include Improve CLI package update behavior when resolving the latest `Volo.Abp.Core` nightly version fails or returns empty: the `--include` pass is now skipped explicitly with a warning while normal project updates continue. Also switch file reading in `VoloNugetPackagesVersionUpdater` to use the shared `DefaultEncoding` instead of `Encoding.Default` for consistent encoding handling. --- .../Cli/ProjectModification/PackagePreviewSwitcher.cs | 10 +++++++++- .../VoloNugetPackagesVersionUpdater.cs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs index 6addbd8bb8..9dfacf5431 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/PackagePreviewSwitcher.cs @@ -273,7 +273,15 @@ public class PackagePreviewSwitcher : ITransientDependency // (source registration / regular PackageReference updates below must still // proceed for every solution/project) - just skip the --include pass. Logger.LogWarning(ex, "Could not resolve the latest Volo.Abp.Core nightly version; --include files will be skipped for this run."); - latestVersionFromMyGet = null; + return (includeFiles, excludedPackages, null); + } + + if (latestVersionFromMyGet.IsNullOrWhiteSpace()) + { + // No exception was thrown, but MyGet simply has no version for this package yet + // (e.g. not indexed there) - warn so users aren't left wondering why --include did nothing. + Logger.LogWarning("Could not resolve the latest Volo.Abp.Core nightly version; --include files will be skipped for this run."); + return (includeFiles, excludedPackages, null); } return (includeFiles, excludedPackages, latestVersionFromMyGet); diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index 6500674799..f9aedb29b9 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -408,7 +408,7 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency { using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { - using (var sr = new StreamReader(fs, Encoding.Default, true)) + using (var sr = new StreamReader(fs, DefaultEncoding, true)) { var fileContent = await sr.ReadToEndAsync(); From 1633bcdc5eb86c086af9fafc5332099c0dd69c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zeynep=20Gizem=20F=C4=B1rat?= Date: Thu, 10 Sep 2026 10:49:07 +0300 Subject: [PATCH 3/5] Preserve project file encoding on update Keep the original project file encoding when rewriting updated NuGet package versions. This avoids forcing the default encoding during XML writes and stores the XML output before truncating the file stream. --- .../ProjectModification/VoloNugetPackagesVersionUpdater.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index f9aedb29b9..70f8ff5755 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -449,12 +449,14 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency } } + var updatedXml = doc.OuterXml; + fs.Seek(0, SeekOrigin.Begin); fs.SetLength(0); - using (var sw = new StreamWriter(fs, DefaultEncoding)) + using (var sw = new StreamWriter(fs, sr.CurrentEncoding)) { - await sw.WriteAsync(doc.OuterXml); + await sw.WriteAsync(updatedXml); await sw.FlushAsync(); } } From e9b3d705346d51ad47b7cb6f0bb9ea301a9a1c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zeynep=20Gizem=20F=C4=B1rat?= Date: Thu, 10 Sep 2026 12:23:36 +0300 Subject: [PATCH 4/5] Make central package version writes atomic Write the updated Directory.Packages.props content to a temp file in the same directory and swap it in with File.Replace instead of truncating the original file in place. A mid-write failure (disk full, process killed) now leaves the original file completely untouched instead of empty or partially written. --- .../VoloNugetPackagesVersionUpdater.cs | 106 ++++++++++-------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs index 70f8ff5755..cad3671ba1 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/VoloNugetPackagesVersionUpdater.cs @@ -406,69 +406,81 @@ public class VoloNugetPackagesVersionUpdater : ITransientDependency try { - using (var fs = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) + string fileContent; + Encoding detectedEncoding; + using (var fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var sr = new StreamReader(fs, DefaultEncoding, true)) { - using (var sr = new StreamReader(fs, DefaultEncoding, true)) - { - var fileContent = await sr.ReadToEndAsync(); + fileContent = await sr.ReadToEndAsync(); + detectedEncoding = sr.CurrentEncoding; + } - var doc = new XmlDocument { PreserveWhitespace = true }; - doc.LoadXml(fileContent); + var doc = new XmlDocument { PreserveWhitespace = true }; + doc.LoadXml(fileContent); - var packageNodeList = doc.SelectNodes("//PackageVersion[starts-with(@Include, 'Volo.')]"); - if (packageNodeList != null) + var packageNodeList = doc.SelectNodes("//PackageVersion[starts-with(@Include, 'Volo.')]"); + if (packageNodeList != null) + { + foreach (XmlNode package in packageNodeList) + { + var packageId = package.Attributes?["Include"]?.Value; + if (packageId == null || excluded.Contains(packageId)) { - foreach (XmlNode package in packageNodeList) - { - var packageId = package.Attributes?["Include"]?.Value; - if (packageId == null || excluded.Contains(packageId)) - { - continue; - } + continue; + } - // LeptonX and Studio packages follow their own, independent version - // stream (see IsLeptonXPackage/IsStudioPackage, also used by - // UpdateVoloPackagesAsync above) - never stamp them with the - // Volo.Abp.Core anchor version, regardless of --exclude-packages. - if (IsLeptonXPackage(packageId) || IsStudioPackage(packageId)) - { - continue; - } + // LeptonX and Studio packages follow their own, independent version + // stream (see IsLeptonXPackage/IsStudioPackage, also used by + // UpdateVoloPackagesAsync above) - never stamp them with the + // Volo.Abp.Core anchor version, regardless of --exclude-packages. + if (IsLeptonXPackage(packageId) || IsStudioPackage(packageId)) + { + continue; + } - var versionAttribute = package.Attributes["Version"]; - if (versionAttribute == null) - { - continue; - } + var versionAttribute = package.Attributes["Version"]; + if (versionAttribute == null) + { + continue; + } - if (versionAttribute.Value != latestVersionFromMyGet) - { - Logger.LogInformation("Updating central package \"{PackageId}\" from v{CurrentVersion} to v{LatestVersion}", packageId, versionAttribute.Value, latestVersionFromMyGet); - versionAttribute.Value = latestVersionFromMyGet; - } - } + if (versionAttribute.Value != latestVersionFromMyGet) + { + Logger.LogInformation("Updating central package \"{PackageId}\" from v{CurrentVersion} to v{LatestVersion}", packageId, versionAttribute.Value, latestVersionFromMyGet); + versionAttribute.Value = latestVersionFromMyGet; } + } + } - var updatedXml = doc.OuterXml; + var updatedXml = doc.OuterXml; - fs.Seek(0, SeekOrigin.Begin); - fs.SetLength(0); + // Write to a temp file in the same directory and atomically swap it in with + // File.Replace, instead of truncating filePath in place - this way a failure + // mid-write (disk full, process killed) never leaves the original file empty + // or partially written; it either stays untouched or is fully replaced. + var tempFilePath = Path.Combine(Path.GetDirectoryName(filePath) ?? string.Empty, Path.GetRandomFileName()); + try + { + using (var tempStream = new FileStream(tempFilePath, FileMode.CreateNew, FileAccess.Write, FileShare.None)) + using (var sw = new StreamWriter(tempStream, detectedEncoding)) + { + await sw.WriteAsync(updatedXml); + await sw.FlushAsync(); + } - using (var sw = new StreamWriter(fs, sr.CurrentEncoding)) - { - await sw.WriteAsync(updatedXml); - await sw.FlushAsync(); - } + File.Replace(tempFilePath, filePath, null); + } + finally + { + if (File.Exists(tempFilePath)) + { + File.Delete(tempFilePath); } } } catch (Exception ex) { - // The file is truncated before the updated XML is written back, so a failure here - // (disk full, process killed, file locked mid-write) can leave it empty/partially - // written on disk. Logged as an error (not a warning) so this isn't missed - the - // rest of the switch-to-nightly run still continues for other solutions/files. - Logger.LogError(ex, "Failed to update central package versions in \"{FilePath}\". The file may now be empty or partially written - please check it manually.", filePath); + Logger.LogError(ex, "Failed to update central package versions in \"{FilePath}\".", filePath); } } } From e8db98f1d9b77b13a334d4a8b090bda7bd3daece Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 11 Sep 2026 15:53:03 +0800 Subject: [PATCH 5/5] Don't fail startup when static web assets can not be loaded --- .../Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs index a457d3b1eb..1fed155274 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/AbpAspNetCoreModule.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; using MyCSharp.HttpUserAgentParser.DependencyInjection; using Volo.Abp.AspNetCore.Auditing; using Volo.Abp.AspNetCore.VirtualFileSystem; @@ -64,7 +65,14 @@ public class AbpAspNetCoreModule : AbpModule context.Services.AddObjectAccessor(); context.Services.AddAbpDynamicOptions(); - StaticWebAssetsLoader.UseStaticWebAssets(context.Services.GetHostingEnvironment(), context.Services.GetConfiguration()); + try + { + StaticWebAssetsLoader.UseStaticWebAssets(context.Services.GetHostingEnvironment(), context.Services.GetConfiguration()); + } + catch (Exception ex) + { + context.Services.GetInitLogger().LogWarning(ex, "Could not load the static web assets manifest, static web assets will not be available. This usually happens when the application runs with build output instead of publish output."); + } context.Services.AddHttpUserAgentCachedParser(); }