github aelassas/servy v7.9
Servy 7.9

5 hours ago

Servy 7.9 introduces a hardened security infrastructure, significant performance optimizations, and a wealth of new features. It's packed with an extensive list of improvements, and the full release notes are available in the expandable section below.

Full Changelog

Click to expand release notes!
  • feat(desktop): replace process parameters input by a resizable textarea (#700)
  • feat(manager): Get CPU and RAM usage of the whole process tree (#446)
  • feat(core): replace WMI with native P/Invoke for improved performance and reliability (#94)
  • feat(notifications): move SMTP settings to external XML config
  • fix(security): ServiceHelper: debug logging can write passwords and sensitive values to plaintext log files (#161)
  • fix(core): Resource leak: StringWriter not disposed in ServiceExporter.ExportXml (#95)
  • fix(core): WindowsServiceApi.GetServices: ServiceController instances not disposed after enumeration (#97)
  • fix(core): HandleHelper.GetProcessesUsingFile: int.Parse can throw FormatException (#98)
  • fix(core): HandleHelper: command injection risk via string-interpolated Arguments (#99)
  • fix(core): RotatingStreamWriter.GenerateUniqueFileName: null-forgiving operator on Path.GetDirectoryName (#101)
  • fix(core): SecureData.Decrypt: silent fallback to plaintext on cryptographic errors (#103)
  • fix(core): Async method naming: Task-returning methods missing Async suffix (#110)
  • fix(core): IServiceManager: mixed sync and async methods in same interface (#114)
  • fix(core): inconsistent return types for operation success across layers (#116)
  • fix(core): ServiceHelper.GetRunningServyServices: verbose list union can be a single LINQ expression (#124)
  • fix(core): EnvironmentVariableParser: list allocated before guard clause - use early return (#128)
  • fix(core): ResourceHelper: verbose conditional list initialization can be a single ternary (#127)
  • fix(core): StringHelper: typo in method name FormatEnvirnomentVariables - missing 'o' (#139)
  • fix(core): InstallServiceAsync: SetServiceDescription called with zero handle when CreateService fails (#158)
  • fix(core): SecureData: key material retained in memory indefinitely - class lacks IDisposable (#159)
  • fix(core): ProtectedKeyProvider: DataProtectionScope.LocalMachine allows any local process to decrypt keys (#160)
  • fix(core): ServiceManager.UninstallServiceAsync: Thread.Sleep blocks thread pool thread in async method (#162)
  • fix(core): RotatingStreamWriter.Rotate: redundant nested lock - caller already holds _lock (#164)
  • fix(core): Logger.Log: null check on _writer outside lock - race condition with Shutdown() (#165)
  • fix(core): RotatingStreamWriter: _disposed check outside lock - race with concurrent Dispose() (#168)
  • fix(core): Helper.IsValidPath: path traversal check is too broad - blocks legitimate paths (#169)
  • fix(core): XXE vulnerability in XmlServiceValidator deserialization (#172)
  • fix(core): SecureData missing ObjectDisposedException guard on Encrypt/Decrypt (#177)
  • fix(core): PID reuse TOCTOU race condition in ProcessKiller (179)
  • fix(core): Race condition between Refresh and Start in ServiceHelper.StartServices (#181)
  • fix(core): unbounded loop in RotatingStreamWriter.GenerateUniqueFileName (#183)
  • fix(core): Credential validation bypassed when password is empty (#182)
  • fix(core): RotatingStreamWriter: uses DateTime.Now instead of UTC for rotation date tracking (#202)
  • fix(core): XmlServiceValidator: XXE vulnerability - XmlDocument loaded without disabling DTD processing (#221)
  • fix(core): EventLogReader: EventRecord objects may reference disposed reader resources (#223)
  • fix(core): EventLogService: XPath injection risk in query filter construction (#224)
  • fix(core): HandleHelper: WaitForExit without timeout - caller blocks indefinitely if handle.exe hangs (#251)
  • fix(core): EventLogService.SearchAsync: unbounded result list - OOM on large event logs (#253)
  • fix(core): ProtectedKeyProvider: no retry on File.ReadAllBytes - AV lock fails service startup (#255)
  • fix(core): RotatingStreamWriter.Rotate: File.Move failure silently skipped - log grows unbounded (#256)
  • fix(core): EventLogReader.ParseLevel: Critical events (level 1) misclassified as Information (#259)
  • fix(core): ProcessHelper.CpuTimesStore: ConcurrentDictionary grows unbounded - memory leak on long-running systems (#264)
  • fix(core): ILogger.Prefix is mutable - thread-safety risk when shared across services (#266)
  • fix(core): HandleHelper.GetProcessesUsingFile: synchronous ReadToEnd() deadlocks when stderr buffer fills (#277)
  • fix(core): ServiceHelper.StopServices: exception wrapping loses inner exception and stack trace (#279)
  • fix(core): Negative RotationSize silently produces huge ulong, disabling log rotation (#280)
  • fix(core): Inconsistent StartupType default: Manual in Repository vs Automatic in Mapper (#291)
  • fix(core): ServiceManager bypasses injected IWin32ErrorProvider in two methods (#295)
  • fix(core): Path.GetDirectoryName and FileInfo.Directory null-forgiving on multiple locations (#307)
  • fix(core): Unchecked enum casts from database integers can produce undefined enum values (#319)
  • fix(core): ResolvePath returns whitespace-only strings as valid paths (#320)
  • fix(core): ServiceDependenciesValidator regex rejects valid service names containing dots (#324)
  • fix(core): ProcessHelper._lastPruneTime: TOCTOU race allows concurrent prune execution (#332)
  • fix(core): ServiceManager.GetAllServices: bare catch silently defaults startup type to Automatic (#337)
  • fix(core): ServiceManager.GetServiceStartupType: catch-all returns ambiguous null (#345)
  • fix(core): ServiceManager.InstallServiceAsync: UpdateServiceConfig return value explicitly discarded (#348)
  • fix(core): Log injection: no newline sanitization on user-controlled strings in log messages (#354)
  • fix(core): Hardcoded DPAPI entropy string in ProtectedKeyProvider weakens defense-in-depth (#355)
  • fix(core): Local privilege escalation: ProgramData directories created without restrictive ACLs (#357)
  • fix(core): SCM and service handles opened with ALL_ACCESS instead of minimum required permissions (#362)
  • fix(core): XML/JSON import validators only check Name and Path - numeric fields not range-validated (#366)
  • fix(core): Encryption key file path revealed in error log and Event Log (#377)
  • fix(core): UserSession property name misleads - actually holds service account name (#380)
  • fix(core): Enum default values differ between ServiceMapper and ServiceRepository for same fields (#401)
  • fix(core): Dead code: IWindowsServiceProvider and WindowsServiceProvider entirely unused (#409)
  • fix(core): DRY: Stop timeout calculation duplicated in 3 locations (#414)
  • fix(core): ProtectedKeyProvider: silent retry loop on DPAPI migration failure - no logging (#415)
  • fix(core): RotatingStreamWriter: failed rotation causes repeated retry on every subsequent write (#418)
  • fix(core): Hidden side-effect: GetCpuUsage mutates global state and prunes dead processes (#432)
  • fix(core): Missing comments: magic number 15 (buffer seconds) used in 5+ locations without explanation (#435)
  • fix(core): Performance: Regex allocated fresh on every HandleHelper.GetProcessesUsingFile call (#438)
  • fix(core): Performance: Logger.WriteLogEntry boxes enum and allocates two strings per log call (#439)
  • fix(core): EventLogService.cs - Bracket heuristic for Servy logs matches unrelated events (#463)
  • fix(core): ServiceConfiguration.cs - Password and ConfirmPassword serialized in plain text during export (#466)
  • fix(core): KillProcessesUsingFile kills by name instead of PID (#472)
  • fix(core): HandleHelper.cs - StandardError redirected but never read, deadlock risk (#475)
  • fix(core): RotatingStreamWriter.cs - EnforceMaxRotations silently swallows File.Delete exceptions (#476)
  • fix(core): ProcessHelper + ProcessKiller - P/Invoke declarations duplicated verbatim (#485)
  • fix(core): Logger.cs - Log entry timestamps always local time regardless of rotation setting (#499)
  • fix(core): Logger.cs - Initialize tears down old writer without draining in-flight writes (#500)
  • fix(core): ServiceManager.cs - Parallel.ForEach parallelism uncapped at ProcessorCount (#504)
  • fix(core): ServiceValidator.cs - Error message says 30s-1h but actual bounds are 1s-86400s (#511)
  • fix(core): ServiceValidator.cs - ExecutablePath not validated in ValidateDto (#512)
  • fix(core): ServiceManager.StartServiceAsync - No differentiation between TimeoutException and other failures (#514)
  • fix(core): ServiceManager.cs - GetAllServices: deeply nested Parallel.ForEach with 3 AllocHGlobal blocks (#520)
  • fix(core): ServiceExporter.cs - XML export declares UTF-16 but file is written as UTF-8 (#555)
  • fix(core): ServiceValidator.cs - Missing upper-bound check on StopTimeout for import flows (#556)
  • fix(core): Handle.cs - Struct wrapping IntPtr is freely copyable, risks double-close (#557)
  • fix(core): NativeMethods.cs - Regex character class [@!-] parsed as ASCII range, allows unintended characters (#563)
  • fix(core): ServiceManager.cs - InstallServiceAsync leaves partial service in SCM on post-create config failure (#564)
  • fix(core): SecureData.cs - Decrypt silently returns plaintext when input is not Base64 (#568)
  • fix(core): Credential validation triggers real domain logon, can lock out service accounts (#574)
  • fix(core): BuildDependencyTree re-expands shared deps in diamond patterns (#578)
  • fix(core): ServiceManager.cs - ArgumentException passes field name as message instead of paramName (#581)
  • fix(core): NativeMethods.cs - ValidateCredentials uses LOGON32_LOGON_NETWORK, false negatives for restricted accounts (#582)
  • fix(core): ServiceHelper.cs - StopServices throws on first failure, remaining services left running (#590)
  • fix(core): ERROR_INVALID_PARAMETER is 7, should be 87 (#657)
  • fix(core): PreviousStopTimeout exported in JSON/XML without JsonIgnore (#658)
  • fix(core): LogonAsServiceGrant.cs - Opens LSA policy with POLICY_ALL_ACCESS instead of minimum rights (#586)
  • fix(core): EnvironmentVariableHelper.cs - Circular reference warning fires falsely on last-iteration convergence (#596)
  • fix(core): Validator accepts newlines as delimiters but parser only accepts semicolons (#599)
  • fix(core): RotatingStreamWriter.cs - EnforceMaxRotations glob pattern can match and delete unrelated files (#603)
  • fix(core): Helper.cs - ParseVersion uses double, minor versions >= 10 sort incorrectly (#629)
  • fix(core): EventLogService.cs - No specific handling for Event Log access failure (#634)
  • fix(core): EventLogService.cs - XPath query manually escaped, injection possible with crafted source name (#635)
  • fix(core): EventLogService.cs - Critical events (level 1) mapped to Error, not separately filterable (#646)
  • fix(core): ProcessKiller.cs - Three different WaitForExit timeouts as unnamed magic numbers (#653)
  • fix(core): AppConfig.cs - "Configration" typo in public API constant names (#656)
  • fix(core): ServyEventLogEntry.cs - No DateTimeKind enforcement on Time property (#659)
  • fix(core): EventLogEntry.cs - Class name collides with System.Diagnostics.EventLogEntry (#661)
  • fix(core): ServiceStatus.cs - Stopped=0 means uninitialized fields default to Stopped instead of Unknown (#663)
  • fix(core): ServiceStartType.cs - AutomaticDelayedStart=5 is not a valid Win32 value, undocumented sentinel (#666)
  • fix(core): Domain/Service.cs - Install() wrapperExeDir parameter silently ignored in RELEASE builds (#667)
  • fix(core): ServiceDependencyNode.cs - ServiceName has no null guard, nullable contract violation (#669)
  • fix(core): Domain/Service.cs - HeartbeatInterval, MaxFailedChecks and other defaults as inline magic numbers (#671)
  • fix(core): JsonServiceValidator/XmlServiceValidator - Log injection via crafted service name (#672)
  • fix(core): IJsonServiceSerializer vs IXmlServiceSerializer - Nullable parameter asymmetry (#673)
  • fix(core): JsonServiceValidator/XmlServiceValidator - No payload size limit before deserialization (#674)
  • fix(core): ILogger.cs - Name collides with Microsoft.Extensions.Logging.ILogger (#677)
  • fix(core): JsonServiceSerializer/XmlServiceSerializer - Deserialize does not catch deserialization exceptions (#680)
  • fix(core,infra): inconsistent ConfigureAwait(false) usage across async methods (#113)
  • fix(core,service): RotationSize: integer overflow in MB-to-bytes conversion (2 locations) (#222)
  • fix(core,service,desktop,cli): Remove commented-out code blocks across 4 files (#137)
  • fix(infra): DummyHelper class is unused - can be removed (#138)
  • fix(infra): ServiceRepository.SearchAsync: null keyword silently matches all records (#234)
  • fix(infra): DapperExecutor: synchronous connection.Open() in all async methods (#236)
  • fix(infra): ServiceRepository: 18 identical Encrypt/Decrypt blocks can use a helper (#245)
  • fix(infra): UpsertAsync ON CONFLICT clause misses Pid, PreviousStopTimeout, ActiveStdoutPath, ActiveStderrPath (#290)
  • fix(infra): System.Data.SQLite 2.0.3: verify bundled SQLite engine version for CVE-2025-6965 (#379)
  • fix(infra): DapperExecutor: no SQLITE_BUSY retry or busy_timeout in connection string (#417)
  • fix(infra): Coupling: ServiceRepository contains domain mapping logic (MapToDomain/MapToDto) (#437)
  • fix(infra): ServiceRepository.cs - UpdateAsync and Update contain identical 40-line SQL blocks (#484)
  • fix(infra): SQLiteDbInitializer.cs - Schema migration via ALTER TABLE with no version tracking (#492)
  • fix(infra): ServiceRepository.cs - 50-column SQL repeated 4x with existing divergence (#493)
  • fix(infra): DapperExecutor.cs - Retry uses Thread.Sleep with linear backoff (#503)
  • fix(infra): UpsertBatchAsync does not sync generated IDs back to DTOs (#585)
  • fix(infra): Column migrations not wrapped in transaction, partial failure possible (#588)
  • fix(infra): IServiceRepository.UpsertBatchAsync - Documents atomic transaction but implementation has none (#670)
  • fix(service): disable recovery when health monitoring is disabled
  • fix(service): prevent integer overflow in health check timer
  • fix(service): Service.cs: blocking Wait() loop without total timeout cap during service stop (#147)
  • fix(service): EnvironmentVariableHelper.ExpandWithDictionary: self-referencing variables cause unbounded string growth (#163)
  • fix(service): Service.cs: _pathValidator missing null check in constructors (#166)
  • fix(service): Race condition on _childProcess field access in Service.cs (#188)
  • fix(service): Process handle leak in fire-and-forget pre-stop hook (#189)
  • fix(service): CancellationTokenSource leak on process restart (#190)
  • fix(service): Fragile reflection on private ServiceBase fields (#192)
  • fix(service): Double-dispose risk and potential exception in ProcessExtensions.GetChildren (#193)
  • fix(service): Hook class: Process object never disposed - native handle leak (#225)
  • fix(service): Path.GetDirectoryName null flows into WorkingDirectory (3 locations) (#239)
  • fix(service): ProcessWrapper.WaitForExit(): parameterless overload can hang service teardown (#252)
  • fix(service): post-launch, failure program, and post-stop hooks don't expand custom environment variables (#260)
  • fix(service): main process env var assignment missing null coalescing (inconsistent with pre-launch) (#261)
  • fix(service): OutputDataReceived race - writer disposed while event handler still in-flight (#262)
  • fix(servce); hardcoded timeout constants should be configurable via appsettings (#268)
  • fix(service): command injection: service name passed unquoted to Servy.Restarter process arguments (#352)
  • fix(service): environment variable override: no blocklist for critical system variables (#353)
  • fix(service): EnableDebugLogs writes environment variables (may contain secrets) to log file in cleartext (#376)
  • fix(service): ProcessWrapper has duplicate Handle and ProcessHandle properties returning same value (#383)
  • fix(service): Hidden side-effect: GetRestartAttempts creates and writes file on read failure (#434)
  • fix(service): Performance: enum-to-string-to-enum round-trip in StartOptionsParser (#440)
  • fix(service): Tech debt: reflection-based ServiceBase field access fragile across .NET versions (#442)
  • fix(service): Abstraction: process launch pattern duplicated across 4 hook methods in Service.cs (#443)
  • fix(service): tech debt: synchronous File.ReadAllText/WriteAllText in Service restart-attempts hot path (#444)
  • fix(service): Service.StartProcess: Process.Start failure leaves service in broken state (#416)
  • fix(service): Dead code: 6 unused Win32 error constants in Errors.cs (#410)
  • fix(service): _isRecovering not volatile, read across threads without synchronization (#451)
  • fix(service): Race between process-exit and health timer can trigger double recovery (#452)
  • fix(service): OnProcessExited uses blocking semaphore Wait on thread pool (#473)
  • fix(service): ProcessExtensions.cs - GetChildren transfers Process ownership without documentation (#481)
  • fix(service): Python/Java detection via .py/.java in Arguments string is fragile (#489)
  • fix(service): Service.cs - Injection constructor skips Logger.Initialize and SecureData creation (#497)
  • fix(service): OnProcessExited early return leaves _isRecovering permanently set (#509)
  • fix(service): _fileSemaphore is static but guards per-instance file paths (#513)
  • fix(service): Environment variable expand+audit pattern duplicated 5 times (#515)
  • fix(service): Service.cs + ProcessLauncher.cs - Python/Java UTF-8 detection duplicated between files (#516)
  • fix(service): Magic threshold 20 in OnStart has no named constant (#521)
  • fix(service): OnCustomCommand silent return when _serviceHandle is IntPtr.Zero leaves SCM waiting (#527)
  • fix(service): CheckHealth holds _healthCheckSemaphore during disk I/O (#528)
  • fix(service): LogUnexpandedPlaceholders emits false-positive warnings for legitimate % usage (#532)
  • fix(service): EnvironmentVariableHelper.cs - Indirect circular env-var references not detected (#545)
  • fix(service): ProcessWrapper.cs - GenerateConsoleCtrlEvent may hit service's own process (#546)
  • fix(service): EnsureJavaUTF8Encoding triggers on .java substring in arguments, false positive for non-Java processes (#567)
  • fix(service): Password masking regex incomplete for quoted and space-separated values (#571)
  • fix(service): Protected-variable blocklist missing runtime-injection variables (#573)
  • fix(service): ExecuteTeardown sets _disposed=true even if Cleanup() throws (#576)
  • fix(service): OnCustomCommand spin-loops with Task.Wait, ThreadPool exhaustion risk (#579)
  • fix(service): StopTree: StopPrivate does not WaitForExit after Kill, grandchild enumeration incomplete (#591)
  • fix(service): RestartService does not kill orphan restarter process on timeout (#592)
  • fix(service): SendCtrlC broadcasts to entire console group, kills unrelated processes (#593)
  • fix(service): _options! null-forgiving in Cleanup() causes NRE if teardown before OnStart completes (#628)
  • fix(service): timeout multiplication int * 1000 can overflow for large configured values (#630)
  • fix(service): ConditionalResetRestartAttemptsAsync fire-and-forget swallows cancellation silently (#631)
  • fix(service): SafeKillProcess accesses faulted task Result, loses inner exception detail (#633)
  • fix(service): InsertPid also sets ActiveStdoutPath/ActiveStderrPath, misleading name (#642)
  • fix(service): Reflection field-search blocks duplicated for _acceptedCommands and status handle (#650)
  • fix(service): reflection-based ServiceBase field access fragile across .NET runtime versions (#652)
  • fix(service): Restarter 240-second timeout as unnamed magic number (#654)
  • fix(service): PreShutdown waitHint and pulse interval as unnamed magic numbers (#655)
  • fix(restarter): ServiceRestarter doesn't handle transitional service states (#191)
  • fix(restarter): missing timeout guard in start phase causes ArgumentOutOfRangeException (#321)
  • fix(restarter): Servy.Restarter exits with code 0 on failure - caller thinks restart succeeded (#333)
  • fix(restarter): Servy.Restarter accepts arbitrary service names without validation (#360)
  • fix(restarter): ServiceRestarter.cs - WaitForStatus(Running) called with potentially negative TimeSpan (#547)
  • fix(desktop): ServiceCommands.InstallService: int.Parse on raw strings can throw FormatException (#228)
  • fix(desktop): MainViewModel: 16 identical Browse methods can be extracted into one helper (#244)
  • fix(desktop): ServiceConfigurationMapper.ToDomain silently drops date rotation settings (#292)
  • fix(desktop): MainViewModel constructor does not null-guard most parameters (#308)
  • fix(desktop): validation logic inconsistency - Servy rejects valid configs when features are disabled (#390)
  • fix(desktop): Servy import path skips most validation - only checks env vars and dependencies (#391)
  • fix(desktop): Servy vs Manager: IServiceCommands return type divergence - Task vs Task (#393)
  • fix(desktop): Magic numbers for defaults in ServiceRepository vs AppConfig constants elsewhere (#398)
  • fix(desktop): Complexity: InstallService method takes 40+ individual parameters (#412)
  • fix(desktop): ServiceCommands.cs - InstallService double-maps config to DTO and options (#488)
  • fix(desktop,manager): App.xaml.cs: ContinueWith error handler runs without explicit TaskScheduler (#146)
  • fix(desktop,manager): ServiceCommands constructors: missing null-guards on injected dependencies (#233)
  • fix(desktop,manager): SecureData not disposed in App startup - key material left in memory (#186)
  • fix(desktop,manager): Process handle leaks in ServiceCommands.OpenManager and ConfigureServiceAsync (#226)
  • fix(desktop,manager): FileDialogService: 6 near-identical dialog methods can share a helper (#246)
  • fix(desktop,manager): HelpService.CheckUpdates: HttpClient has no timeout - UI can hang indefinitely (#250)
  • fix(desktop,manager): WPF UI: missing AutomationProperties - not accessible to screen readers (#267)
  • fix(desktop,manager): Process.Start() return value not checked when launching external apps (#349)
  • fix(desktop,manager): Servy vs Manager: success message inconsistency - Msg_ServiceCreated vs Msg_ServiceInstalled (#395)
  • fix(desktop,manager): scalability: BulkObservableCollection.TrimToSize is O(n²) due to RemoveAt(0) (#427)
  • fix(desktop,manager): App.xaml.cs - Global exception handler exposes raw exception details to UI (#470)
  • fix(desktop,manager): HelpService.cs - OpenDocumentation has no exception handling (#482)
  • fix(desktop,manager): App.xaml.cs (both) - Duplicate DB/SecureData initialization in App and MainWindow (#494)
  • fix(desktop,manager): HelpService.cs - Hardcoded English UI strings instead of localized resources (#496)
  • fix(desktop,manager): Both App.xaml.cs - Exception handlers registered inside fire-and-forget InitializeApp, crash window (#606)
  • fix(desktop,manager): BulkObservableCollection.cs - _suppressNotification not volatile, cross-thread visibility issue (#622)
  • fix(desktop,manager): HelpService.cs - new HttpClient() per call, socket exhaustion anti-pattern (#643)
  • fix(desktop,manager): DependenciesView/PerformanceView/ConsoleView - async void Loaded handlers with no try/catch (#664)
  • fix(desktop,manager): RelayCommand - Unsafe unboxing cast when parameter is null (#668)
  • fix(manager): HistoryResult.cs - Lines property exposes mutable List (#675)
  • fix(desktop,manager): MessageBoxService.cs - BeginInvoke returns before dialog is dismissed (#676)
  • fix(desktop,manager): AsyncCommand._isExecuting - Not volatile, thread-safety gap (#682)
  • fix(desktop,manager): IAsyncCommand.ExecuteAsync - Non-nullable parameter vs AsyncCommand nullable parameter (#683)
  • fix(desktop,manager,cli): JSON deserialization should explicitly set TypeNameHandling.None (#175)
  • fix(desktop,manager,service): missing constructor null guards in 5 classes (pattern inconsistency) (#241)
  • fix(desktop,manager,service): HelpService and ServiceHelper: Process.Start return value not disposed (3 locations) (#242)
  • fix(desktop,manager,cli): Process.MainModule!.FileName can be null in single-file deployments and restricted contexts (#306)
  • fix(desktop,manager,cli): No pre-flight administrator check before privileged SCM and LSA operations (#361)
  • fix(desktop,manager,cli): No file size check before reading import files - potential OOM denial-of-service (#364)
  • fix(desktop,manager,cli): no string length bounds on service name, display name, description, and parameters (#367)
  • fix(desktop,manager,cli): DatabaseValidator - SQLite version check is advisory only, does not halt startup (#524)
  • fix(manager): hardened bulk service operations with dynamic hardware-aware throttling
  • fix(manager): MainViewModel: three near-identical bulk service operations can be extracted to one method (#122)
  • fix(manager): MainViewModel.UpdateSelectAllState: two All() iterations can be replaced by single Count() (#123)
  • fix(manager): ConsoleViewModel: fire-and-forget task without error handling on log tailing (#143)
  • fix(manager): PerformanceViewModel, DependenciesViewModel, ConsoleViewModel: missing Cleanup() - timer and CTS resource leaks (#144)
  • fix(manager): LogTailer: potential integer overflow in DateTime.AddTicks() for synthetic timestamps (#145)
  • fix(manager): ConsoleViewModel.OnTickAsync: bare catch silently hides all errors (#148)
  • fix(manager): LogTailer.LoadHistory: no boundary validation on maxLines parameter (#149)
  • fix(manager): ViewModels: missing null-conditional on _timer.Stop() and _timer.Start() calls (#150)
  • fix(manager): Event handler memory leak in ServiceRowViewModel (#184)
  • fix(manager): RequestScroll event handler never unsubscribed in ConsoleView (#185)
  • fix(manager): bare catch blocks silently swallow all exceptions in timer handlers (#187)
  • fix(manager): LogTailer.RunFromPosition: bare catch blocks swallow exceptions without logging (#199)
  • fix(manager): ConsoleViewModel: uses static Logger class instead of injected _logger instance (#200)
  • fix(manager): Manager ServiceCommands.RefreshServices: fire-and-forget async callback - exceptions unobserved (#232)
  • fix(manager): ConsoleViewModel: NullReferenceException in CollectionView filter (as cast without null check) (#237)
  • fix(manager): Manager ServiceCommands.GetServiceDomain: search-based lookup instead of exact match (#229)
  • fix(manager): MainWindow.xaml.cs: potential NullReferenceException on _messageBoxService in catch block (#230)
  • fix(manager): App.xaml.cs: Path.GetDirectoryName can return null - NRE in Path.Combine (#238)
  • fix(manager): PerformanceViewModel: Pid.Value crash due to race between currentSelection and SelectedService (#240)
  • fix(manager): Redundant CTS null + IsCancellationRequested check before Cancel() (3 locations) (#247)
  • fix(manager): MainViewModel: .Count(predicate) == .Count can be replaced with .All() (#248)
  • fix(manager): LogTailer.LoadHistory: tempLines list ignores maxLines cap during ReadLine loop (#254)
  • fix(manager): ServiceCommands: concurrent Start/Stop/Restart can corrupt service.Status (#257)
  • fix(manager): LogTailer: off-by-one in synthetic timestamp - last line gets lastWrite-1ms instead of lastWrite (#258)
  • fix(manager): CancellationTokenSource leaks and race conditions in Manager ViewModels (#282)
  • fix(manager): Application.Current can be null during shutdown in background tasks (#284)
  • fix(manager): async void SwitchService can crash the app if inner catch throws (#286)
  • fix(manager): ServiceMapper.ToModelAsync hardcodes IsInstalled=false and Status=None (#300)
  • fix(manager): ServiceCommands.CopyPid: race condition between null check and Clipboard.SetText (#310)
  • fix(manager): MainWindow: ServiceCommands is null during ViewModel construction window (#311)
  • fix(manager): SetPidText() accesses SelectedService without null guard (#317)
  • fix(manager): Empty services collection causes SelectAll checkbox to show as checked (#327)
  • fix(manager): MainViewModel: Service model PropertyChanged fires from background threads - collection race (#329)
  • fix(manager): MainViewModel: Cleanup/CreateAndStartTimer race - old refresh task gets new uncancelled token (#330)
  • fix(manager): MainViewModel.Resfresh() - typo in method name (transposed s and f) (#382)
  • fix(manager): DependenciesViewModel and DependencyService: copy-pasted docs reference log tailing (#384)
  • fix(manager): inconsistent logging levels - Logger.Error vs _logger.Warn for same operations (#392)
  • fix(manager): ServiceCommands depends on concrete ServiceManager instead of IServiceManager (#394)
  • fix(manager): Performance: two Process.GetProcessById calls per service per tick (#419)
  • fix(manager): Performance: three tab ViewModels each query full DTO just to check PID (#421)
  • fix(manager): Performance: 4 new PointCollection allocations per tick in PerformanceViewModel (#422)
  • fix(manager): ConsoleViewModel: full log scan on every search keystroke - no debounce (#423)
  • fix(manager): memory leak: DispatcherTimer.Tick never unsubscribed in ViewModel Cleanup methods (#424)
  • fix(manager): memory leak: RemoveService does not unsubscribe PropertyChanged on removed VM (#425)
  • fix(manager): Scalability: N individual SQLite UPSERTs per refresh tick - not batched (#426)
  • fix(manager): Scalability: ServicesView.Refresh() full rebuild every tick with large service count (#428)
  • fix(manager): hidden side-effect: RefreshServiceInternal writes to database during UI refresh (#433)
  • fix(manager): Tech debt: SemaphoreSlim(1,1) serializes ALL service commands behind one lock (#441)
  • fix(manager): console view fails to load logs when stdout redirect is missing (#448)
  • fix(manager): MainViewModel.cs - _servicesLock not used in SearchServicesAsync (#464)
  • fix(manager): ServiceCommands.cs - Service name embedded in process arguments without escaping (#468)
  • fix(manager): LogsViewModel.cs - CancellationTokenSource lifecycle issues (#479)
  • fix(manager): LogTailer.cs - Rotation detection via CreationTimeUtc unreliable on some file systems (#480)
  • fix(manager): PerformanceViewModel.cs - AddPoint calls RemoveAt(0) on List, O(n) per tick (#519)
  • fix(manager): MainWindow.xaml.cs - OnClosed does not call Cleanup on child ViewModels (#525)
  • fix(manager): ServiceCommands.cs - _serviceLocks semaphores never removed or disposed (#531)
  • fix(manager,core): multiple files - Magic numbers without named constants (batch) (#522)
  • fix(manager): PerformanceViewModel.cs - Stringly-typed dispatch in AddPoint via propertyName (#523)
  • fix(manager): Manager MainViewModel.cs - RemoveService mutates ObservableCollection off UI thread (#529)
  • fix(manager): MainWindow.xaml.cs - HandlePerfTabSelected + duplicate StartMonitoring calls (#595)
  • fix(manager): DependenciesViewModel.cs - LoadDependencyTree blocks UI thread with synchronous SCM calls (#602)
  • fix(manager): ServiceCommands.cs - GetServiceDomain passes null DTO to ToDomain when service not in DB (#613)
  • fix(manager): MainViewModel.cs - Dispatcher.Invoke (blocking) inside parallel loop causes thread starvation (#616)
  • fix(manager): PerformanceViewModel.cs - valueHistory.Max() O(n) scan on every UI timer tick (#636)
  • fix(manager): MainViewModel.cs - Services added one-by-one to ObservableCollection, floods CollectionChanged (#638)
  • fix(manager): LogTailer.cs - No IDisposable, event handler references keep ViewModel rooted (#640)
  • fix(manager): CpuUsageConverter/RamUsageConverter - ToString() round-trip uses CurrentCulture, comma decimal breaks parse (#644)
  • fix(manager): ConsoleViewModel.cs - LINQ sort with anonymous objects on service switch causes GC pressure (#645)
  • fix(manager): ServiceCommands.cs - _serviceLocks ConcurrentDictionary grows without bound (#647)
  • fix(manager): ConsoleViewModel.cs - SelectedService setter triggers file I/O and timer restart as hidden side effects (#651)
  • fix(manager): Service.cs - Name property not change-notified via PropertyChanged (#662)
  • fix(manager): ServiceMapper.cs (Manager) - Hard cast (App)Application.Current in static mapper, untestable (#665)
  • fix(manager): IServiceConfigurationValidator - Namespace/folder mismatch (#678)
  • fix(manager): LogsView.xaml.cs - Missing Unloaded event unsubscribe, memory leak (#679)
  • fix(manager,cli): Export functionality writes plaintext service account passwords to XML/JSON files (#375)
  • fix(cli): misleading success message for import and export commands
  • fix(cli): log exceptions in case unexpected errors
  • fix(cli): ServiceInstallValidator: operator precedence bug in timeout/retry validation (#96)
  • fix(cli): ImportServiceCommand: no path validation on deserialized service configuration (#100)
  • fix(cli): ExportServiceCommand: redundant inner try-catch and misspelled error message (#112)
  • fix(cli): CLI Commands: inconsistent constructor null validation - 6 of 8 commands skip checks (#111)
  • fix(cli): inconsistent logging - 4 of 8 commands have no log statements (#115)
  • fix(cli): CLI Commands: inconsistent input validation approach - validator class vs inline checks (#117)
  • fix(cli): Path traversal in CLI export command (#173)
  • fix(cli): Path traversal in CLI import command (#174)
  • fix(cli): CLI ServiceInstallValidator: EnableSizeRotation bypasses RotationSize validation (#231)
  • fix(cli): CLI commands: inconsistent validation approach - injected validator vs inline checks with mixed error messages (#263)
  • fix(cli): error messages lack context - no paths, no operation names (#265)
  • fix(cli): ExportServiceCommand: path traversal protection bypassable via NTFS junctions (#283)
  • fix(cli): Helper.PrintAndReturn vs PrintAndReturnAsync: inconsistent exit code behavior (#293)
  • fix(cli): res.ErrorMessage! null-forgiving on potentially null ErrorMessage (#312)
  • fix(cli): export command allows UNC paths and Windows device paths - data exfiltration risk (#368)
  • fix(cli): Dead code: PrintAndReturn (sync) never called; ExitCode property only consumer (#411)
  • fix(cli): RecoveryAction accepted without EnableHealthMonitoring (#594)
  • fix(cli): InstallServiceOptions.cs --password CLI flag exposes password in OS process listing (#637)
  • fix(cli): ExportServiceCommand.cs - Missing EnsureAdministrator check (#639)
  • fix(cli): ImportServiceCommand.cs - Admin check skipped when --install is omitted (#641)
  • fix(cli): ConsoleHelper.cs - Console.WindowWidth throws IOException when stdout is redirected (#681)
  • fix(cli,psm1): ConsoleHelper and Servy.psm1: string concatenation with + instead of interpolation (#129)
  • fix(cli,infra): Mixed case comparison: StringComparison.OrdinalIgnoreCase vs ToLowerInvariant() in same files (#400)
  • fix(psm1): misleading error message when Servy CLI is not found (#50)
  • fix(psm1): typo in PreStopLogAsError parameter documentation (#60)
  • fix(psm1): Test-ServyCliPath: error message shows wrong search path on 64-bit systems (#61)
  • fix(psm1): Invoke-ServyCli: potential deadlock when reading stdout and stderr synchronously (#62)
  • fix(psm1): servy-module-examples.ps1: relative Import-Module path fails when current directory differs (#64)
  • fix(psm1): Test-ServyCliPath: duplicate .SYNOPSIS help block (#66)
  • fix(psm1): Install-ServyService: switch parameters bypass Add-Arg helper function (#67)
  • fix(psm1): Install-ServyService: numeric parameters typed as [string] instead of [int] (#68)
  • fix(psm1): Export-ModuleMember is ignored when module is loaded via manifest (#70)
  • fix(psm1): Show-ServyVersion: documentation says --version but command sends version (#71)
  • fix(psm1): five service control functions are near-identical copy-paste (#72)
  • fix(psm1): Install-ServyService: 56 repetitive Add-Arg calls could be a hashtable loop (#73)
  • fix(psm1): Add-Arg: unnecessary unary comma on return statement (#75)
  • fix(psm1): same boilerplate as service control functions (#76)
  • fix(psm1): function export list maintained in three separate locations (#77)
  • fix(psm1): module description duplicated across three files (#78)
  • fix(psm1): CLI path validated at module load and again at every function call (#79)
  • fix(psm1): servy-module-examples.ps1: help block examples duplicate module-level examples in Servy.psm1 (#80)
  • fix(psm1): Invoke-ServyCli: WaitForExit() blocks indefinitely if servy-cli.exe hangs (#81)
  • fix(psm1): Invoke-ServyCli: Process.Start() return value ignored (#82)
  • fix(psm1): Invoke-ServyCli: no null check on StandardOutput and StandardError before ReadToEnd() (#83)
  • fix(psm1): potential null reference in Get-Command pathSearch fallback (#88)
  • fix(psm1): Invoke-ServyCli: ReadToEnd() exception loses CLI output context on process crash (#90)
  • fix(core): ProcessKiller: bare catch blocks silently swallow all exceptions (#102)
  • fix(psm1): Show-ServyHelp: -Command parameter ignored, undefined $argsList passed to CLI (#104)
  • fix(psm1): Install-ServyService: PreLaunchRetryAttempts parameter is [string] instead of [int] - no input validation (#106)
  • fix(psm1): Install-ServyService: $Env parameter shadows PowerShell $env: automatic variable (#107)
  • fix(psm1): Invoke-ServyCli: stderr may be incomplete due to missing async flush after WaitForExit (#108)
  • fix(psm1): Invoke-ServyCli: global stderr variable may persist after early exception (#109)
  • fix(psm1): Export-ServyServiceConfig: missing [ValidateNotNullOrEmpty()] on $Path parameter (#118)
  • fix(psm1): only Invoke-ServyServiceCommand has [CmdletBinding()] - missing on all public functions (#119)
  • fix(psm1): inconsistent comment-based help indentation across functions (#120)
  • fix(psm1): Invoke-ServyCli: exception error message missing space separator (#121)
  • fix(psm1): five identical single-command functions can share a common body (#130)
  • fix(psm1): Add-Arg null check on $list is dead code - callers always pass @() (#131)
  • fix(psm1): Invoke-ServyCli: verbose argument list construction can be simplified (#132)
  • fix(psm1): Invoke-ServyCli: unnecessary pre-initialization of $stdout, $stderr, and $exitCode (#133)
  • fix(psm1): Install-ServyService: [int] parameters with default 0 are sent to CLI even when not specified (#134)
  • fix(psm1): redundant double check on Get-Command result (#135)
  • fix(psm1): stale FIX comment on line 332 can be removed (#140)
  • fix(psm1): unnecessary "Assuming" comment about Add-Arg on line 329 (#141)
  • fix(psm1): excessive inline comments in Add-Arg repeat what the code already says (#142)
  • fix(psm1): Invoke-ServyCli: silent catch on process Kill after timeout - orphaned process possible (#152)
  • fix(psm1): Invoke-ServyCli: no TOCTOU check on ServyCliPath before process start (#153)
  • fix(psm1): Invoke-ServyCli: $Command joined into argument string without quoting (#154)
  • fix(psm1): module-load throw is not PowerShell-idiomatic - prevents ErrorAction handling (#155)
  • fix(psm1): Invoke-ServyCli: stderr event scriptblock built via fragile string interpolation (#156)
  • fix(psm1): Add-Arg: trailing backslash in paths breaks Windows command-line argument parsing (#157)
  • fix(psm1): service account password visible in process command-line arguments (#170)
  • fix(psm1): Add-Arg: multiple trailing backslashes still break argument parsing (#171)
  • fix(psm1): PowerShell module: Incomplete argument escaping in Add-Arg allows argument injection (#195)
  • fix(psm1): Add-Arg escape overwrite bug - line 131 processes $value instead of $escapedValue (#197)
  • fix(psm1): Invoke-ServyCli: exit code check after finally block - stderr may be lost (#205)
  • fix(psm1): Install-ServyService: PSBoundParameters key matching relies on implicit case-insensitivity (#206)
  • fix(psm1): Add-Arg: no type constraint on $list parameter (#207)
  • fix(psm1): Add-Arg: array concatenation is O(n²) - consider ArrayList (#208)
  • fix(psm1): Add-Arg: add fast-path for values without special characters (#209)
  • fix(psm1): ValidateNotNullOrEmpty on $Name parameter (#210)
  • fix(psm1): Servy.psm1: formatting inconsistencies across functions (#211)
  • fix(psm1): Invoke-ServyCli: stderr race condition after Kill() - async events not flushed (#212)
  • fix(psm1): Invoke-ServyCli: event handler registered before global variable exists (#213)
  • fix(psm1): Install-ServyService: $EnvVars parameter name mismatches CLI option --env - PSBoundParameters check broken (#214)
  • fix(psm1): Invoke-ServyCli: parameterless WaitForExit() on line 280 can hang indefinitely (#215)
  • fix(psm1): Invoke-ServyCli: no validation on $script:ServyTimeoutSeconds (#216)
  • fix(psm1): Invoke-ServyCli: missing CancelErrorRead() in finally block (#217)
  • fix(psm1): Invoke-ServyCli: StandardOutput.ReadToEnd() could exhaust memory on large output (#218)
  • fix(psm1): Invoke-ServyCli: Process.Start() failure handling could include Win32 error details (#219)
  • fix(psm1): Show-ServyHelp: no explicit --help flag sent when called without -Command (#220)
  • fix(psm1): Install-ServyService: .PARAMETER Env help text orphaned after rename to $EnvVars (#269)
  • fix(psm1): no guard on total argument string length - cryptic error at Windows 32K limit (#270)
  • fix(psm1): race between CancelErrorRead and finally-block ArrayList read (#271)
  • fix(psm1): functions document 'Requires Administrator' but never verify elevation (#272)
  • fix(psm1): Install-ServyService: 14 path and 3 format parameters lack early validation (#273)
  • fix(psm1): CLI stderr containing password can leak into PS error messages and $Error (#274)
  • fix(psm1): module-load guard blocks all unit testing on machines without servy-cli.exe (#275)
  • fix(psm1): Add-Arg: $key.Trim() called 3 times per invocation - trim once at entry (#276)
  • fix(psm1): Add-Arg: -Flag path missing return causes duplicate arguments (#325)
  • fix(psm1): Invoke-ServyCli: empty catch on process kill hides failure reason (#346)
  • fix(psm1): $PreLaunchEnv lacks pattern validation unlike $EnvVars (#369)
  • fix(psm1): $DisplayName and $Password parameters lack basic validation (#372)
  • fix(psm1): Export/Import -Path parameters lack existence validation (#373)
  • fix(psm1): Show-ServyVersion and Show-ServyHelp use Show- verb but return pipeline output (#388)
  • fix(psm1): Password parameter is [string] not [SecureString] (#469)
  • fix(psm1): hidden coupling between PS parameter names and CLI flag names (#495)
  • fix(psm1): Start-Sleep 50ms instead of proper WaitForExit for stderr flush (#501)
  • fix(psm1): TrimStart casing mismatch makes PSBoundParameters.ContainsKey always fail (#510)
  • fix(psm1): Error throw uses $_ instead of $_.Exception.Message (#584)
  • fix(psd1): add license and help urls
  • fix(tests): tests/test.ps1: missing $LASTEXITCODE checks after dotnet test and reportgenerator (#342)
  • fix(tests): coverage collection only includes 2 of 8 test assemblies (#553)
  • fix(notifications): unnecessary Sort-Object in failure notification scripts (#63)
  • fix(notifications): ServyFailureEmail.ps1: hardcoded plaintext SMTP credentials without security warning (#65)
  • fix(notifications): ServyFailureEmail.ps1 and ServyFailureNotification.ps1: duplicated event log query and parsing logic (#74)
  • fix(notifications): ServyFailureEmail.ps1: Send-MailMessage has no error handling (#84)
  • fix(notifications): Get-WinEvent has no error handling (#85)
  • fix(notifications): ServyFailureNotification.ps1: WinRT type loading and toast display have no error handling (#86)
  • fix(notifications): ServyFailureNotification.ps1: toast notification fails silently in non-interactive sessions (#89)
  • fix(notifications): taskschd: "latest error" query misses simultaneous service failures (#91)
  • fix(notifications): taskschd: MultipleInstancesPolicy=IgnoreNew silently drops error notifications during burst failures (#92)
  • fix(notifications): taskschd: VBS fire-and-forget wrappers create orphaned processes and hide failures (#93)
  • fix(notifications): ServyFailureEmail.ps1: hardcoded placeholder SMTP credentials (#201)
  • fix(notifications): ServyFailureEmail.ps1: Send-MailMessage -UseSsl requires PowerShell 3.0+ (#204)
  • fix(notifications): ServyFailureNotification.ps1: WinRT toast notifications require PowerShell 5.0+ (#203)
  • fix(notifications): ServyFailureNotification.ps1: PS 5.0+ syntax breaks declared PS 2.0 compatibility (#235)
  • fix(notifications): Get-ServyLastErrors.ps1: $ModuleRoot variable undefined - writes log to wrong location (#313)
  • fix(notifications): ServyFailureEmail.ps1: XML config properties accessed without null checks (#314)
  • fix(publish): publish-sc.ps1: Resolve-Path leaves variables null when files are missing (#315)
  • fix(notifications): ServyFailureNotification.ps1: XML toast nodes can be null after Where-Object (#318)
  • fix(notifications): ServyFailureEmail.ps1: HTML injection in notification email body (#370)
  • fix(notifications): ServyFailureEmail.ps1: SMTP config From/To not validated for email format (#374)
  • fix(notifications): Failure notification scripts forward event log content unfiltered via email and toast (#378)
  • fix(notifications): Task scheduler scripts use $ModuleRoot variable name in non-module .ps1 scripts (#389)
  • fix(noticiations): PowerShell task scheduler scripts deviate from codebase conventions (#403)
  • fix(notifications): ServyFailureEmail.ps1 - System.Net.WebUtility unavailable on .NET 3.5 (PS 2.0 target) (#609)
  • fix(notifications): Get-ServyLastErrors.ps1 - Get-WinEvent requires Vista+, not available on XP/Server 2003 (#611)
  • fix(notifications): ServyFailureEmail/Notification.ps1 - Sort-Object result not wrapped in @(), scalar on single event (#614)
  • fix(notifications): Get-ServyLastErrors.ps1 - exit 1 inside dot-sourced function kills caller's session (#627)
  • fix(publish): build scripts use PowerShell 3.0+/5.0+ features despite PS 2.0 compatibility target (#288)
  • fix(publish): Servy.Service/publish.ps1 passes parameters that child scripts silently ignore (#298)
  • fix(publish): Project publish scripts: dotnet restore/clean/publish and signing missing $LASTEXITCODE checks (#351)
  • fix(publish): Setup publish scripts: $Version parameter lacks format validation - Inno Setup injection (#371)
  • fix(publish): Setup publish scripts use -Fm parameter while child scripts use -Tfm (#386)
  • fix(publish): $selfContained and $serviceProject assigned but never used (#387)
  • fix(publish): Publish scripts: inconsistent patterns between Service/Restarter vs CLI/Manager/Servy (#396)
  • fix(publish): PowerShell: mixed MSBuild property syntax -p: and /p: in same dotnet publish commands (#402)
  • fix(publish): publish-res scripts - Resolve-Path before Test-Path causes build failure on clean checkout (#549)
  • fix(publish): publish-fd.ps1 and publish-sc.ps1 - No existence check before Copy-Item for CLI artifacts (#550)
  • fix(publish): Servy.Manager/publish.ps1 - Missing exe guard before signing, unlike other publish scripts (#551)
  • fix(publish): Servy.Service/publish.ps1 -Runtime passed as bare switch to callee that has no such parameter (#597)
  • fix(publish): Error message references wrong binary name (copy-paste error) (#605)
  • fix(publish): missing #requires -Version 3.0 where $PSScriptRoot is used (#620)
  • fix(publish): All publish-res-*.ps1 - Resolve-Path throws before Test-Path guard is reached (#623)
  • ci: add security audit workflow for dependency, static, and secret scanning
  • ci(bump-runtime): Get-Item on hardcoded paths throws if files are missing (#316)
  • ci(bump-version): $appConfigPath variable silently reused for unrelated file (#385)
  • ci(bump-version): WriteAllText may add UTF-8 BOM, unlike bump-runtime.ps1 (#502)
  • ci(bump-version): Get-FileEncoding does not detect UTF-16, can corrupt project files (#625)
  • ci(actions): actions/checkout@v5, @v6, and upload-artifact@v6 may not exist (#548)
  • ci(actions): Third-party actions pinned to floating @master/@main branches (#554)
  • ci(sbom): workflow_dispatch input injected directly into PowerShell script string (#577)
  • ci(scoop): branch name mismatch causes git reset to wrong ref (#565)
  • ci(bump-version): git diff --quiet misses untracked files, version bump commit silently skipped (#566)
  • ci(scoop): PAT embedded in git clone URL, stored in .git/config (#580)
  • ci(publish): SEVEN_ZIP env var set at workflow level but overridden per-step, confusing default (#600)
  • ci(dependabot): GitHub Actions ecosystem not monitored (#608)
  • ci(choco): Commit-then-rebase-push pattern is race-prone (#612)
  • ci(bump-version): workflow_run trigger runs even when choco workflow fails (#615)
  • ci(publish,sbom): publish.yml / sbom.yml - CycloneDX CLI version drift between workflows (#618)
  • ci(publish): no timeout-minutes on job, signing steps can hang indefinitely (#621)
  • ci(scoop): force push to fork branch without checking if PR already merged (#624)
  • ci(loc): loc.yml peaceiris/actions-gh-pages needs contents:write but no permissions declared (#626)
  • chore(deps): update dependencies

Don't miss a new servy release

NewReleases is sending notifications on new releases.