Repository navigation
Give Lite a switch for the startup update check (#2413) - #2415
Conversation
Five seconds after every launch, MainWindow.CheckForUpdatesOnStartupAsync calls github.com looking for a new version. It is gated on App.CheckForUpdatesOnStartup, which defaults to true and was drawn in no window, so the only supported way to refuse an unsolicited outbound call from a DBA workstation was to hand-edit check_for_updates_on_startup into settings.json -- a key that appears in no documentation and, as it turns out, in no sample file either, because Lite/config/settings.json is CopyToOutputDirectory=Never and is not in ConfigSeeder's seed list, so it ships nowhere and seeds nothing. The population running this product skews toward locked-down networks where that call is somebody's business, and several of those users cannot reach github.com at all, so it has been failing silently for them with nothing to explain why the update banner never appears. The discoverability side cuts the same way: a user who WANTS the notifications had no way to confirm they were on. The checkbox is the whole change. The default stays true, which is defensible with a visible switch beside it; whether it should stay true is a product question about how users learn a release exists, and it belongs in its own issue rather than being decided here as a side effect. The key name is unchanged, so anyone who already hand-edited it keeps their setting, and the writer merges into the parsed document rather than rewriting it, so nothing else in the file moves. It is deliberately not wired through SaveAlertSettings, even though it sits beside Minimize to tray, which is. This is application behavior rather than alerting, and being in the alert block would mean two wrong things: it would gray out with the "Enable notifications" master switch, claiming a dependency the update check does not have, and Restore Defaults would stomp it. So it takes the shape the Dashboard Defaults knobs already use -- a LoadCheckForUpdates seeded from the static, a SaveCheckForUpdates called from the Save button, and one App.WriteSetting merge into settings.json. That also puts it outside the scope of LiteSaveAlertSettings_PersistsEveryStaticItAssigns, which is the correct answer rather than a dodge: that guard's subject is the alert block, and the Alert*Box gate/reset guard beside it never reaches a checkbox at all. AboutWindow's check stays unconditional. Opening Help > About is asking, and the setting governs the call the user did not ask for. Fixes #2413 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lite has no Help menu. About is a sidebar button beside Settings, and the "Help > About" the title-bar suffix and the tray balloon both promise has never been a real path. My tooltip repeated it, which would have shipped a brand new wrong string in the one place a user turning the check OFF looks to find out where the deliberate check still lives. The three pre-existing strings in MainWindow are left alone here and filed separately rather than folded in -- they are a different lane's one-line change and this diff is meant to stay a Settings-window diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Follow-up commit f351c3e: my tooltip originally said the About check still runs from "Help > About". Lite has no Help menu — The three pre-existing strings that make the same claim ( |
|
Reviewed. Small, well-scoped change — verified against the code:
No correctness, parity, or security issues found. This isn't T-SQL so the collector-query style rules (OPTION(RECOMPILE), etc.) don't apply here. |
…ingdata#2418) Lite/config/settings.json shipped nowhere, seeded nothing and was read by nothing, and it had drifted further than the report said. Four of its eight keys are read by no code anywhere in the repo -- not just "theme", which the loader has always spelled color_theme, but also hot_data_retention_days, archive_retention_days and start_collection_on_launch, none of which appear in a single .cs file. Half the template was fiction, and the four keys that were real are read by three different loaders, only one of which the file's position beside collection_schedule.json would suggest. Of the two options, deleting it is the cheaper answer and it is the wrong one, because it does not address what the file was for. Lite reads 88 keys out of settings.json and four of them have no control in the Settings window at all: analysis_timeout_seconds, and check_for_updates_on_startup until erikdarlingdata#2415 lands, plus the two legacy plaintext webhook URLs erikdarlingdata#1506 migrates. So "the Settings window is the discovery path" is true for 84 keys and false for the ones a user is most likely to need help with, and deleting the file leaves the only route to them being to read App.xaml.cs. Darling already carries the honest version of this in darling.sample.json, and erikdarlingdata#2389 was decided on exactly that asymmetry: the sample described the store-vs-file split correctly while the runtime said nothing about it. So the file is now a real commented reference to all 88 keys, with each one's default, its clamp range where the loader clamps on read, and its legal values where the loader validates. It is renamed to settings.sample.json and switched to PreserveNewest. Both halves matter. The old name was the live file's name, sitting among two siblings that ARE seeded, which is what made it look like a seed rather than a document; and shipping it beside the exe is the difference between a reference for people reading GitHub and one for people editing an install. It stays out of ConfigSeeder's list deliberately: it is documentation to read, not a file to drop in, and its header says so, because Lite's own loader rejects comments and rewrites the whole document on every Save. The test is the part that makes this worth keeping rather than a better-looking version of the same trap. It derives the key list by regex from the shipped App.xaml.cs and Mcp\McpSettings.cs, copied beside the test binary, and fails in both directions: a key read but not documented, and a key documented but read by nothing. Each TryGetProperty is attributed to the nearest *.json literal above it, so a loader added later for servers.json or collection_schedule.json is excluded by construction instead of needing an exemption. Both exemption sets are therefore EMPTY, and the comments on them say what would justify adding one. A fourth test pins that the extraction still finds the loaders at all, because the regex is the whole test and a restructured loader would otherwise make the symmetry checks pass on an empty set. The dead "theme" key is discussed in a sample COMMENT rather than carried as a key, so the warning reaches anyone who used the old template without the sample having to claim a key nothing reads. Verified by compiling the real test file into a net10.0 console harness with an xUnit shim -- the shipped test source, not a transcription -- since Lite.Tests is net10.0-windows and cannot run on macOS. It passes on this branch and fails on dev's file, naming all four dead keys and all 84 undocumented ones. Two single-key mutations confirm it is not merely a bulk-mismatch detector: adding one undocumented key to the loader is caught by name, and renaming color_theme to theme in the sample -- this issue's original defect, reproduced -- fails both directions at once. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixes #2413.
Five seconds after every launch,
MainWindow.CheckForUpdatesOnStartupAsynccalls github.com looking for a new version. It is gated onApp.CheckForUpdatesOnStartup, which defaults totrueand was drawn in no window, so the only supported way to refuse an unsolicited outbound call from a DBA workstation was to hand-editcheck_for_updates_on_startupinto settings.json. This adds the checkbox, and that is the whole change.The issue said the key "appears in no sample file", and that turns out to be stronger than it reads:
Lite/config/settings.jsonis the only thing resembling a sample, and it isCopyToOutputDirectory=Neverand not inConfigSeeder's seed list, so it ships nowhere and seeds nothing. It also still carries"theme": "light"where the loader readscolor_theme. I left it alone rather than adding a key to a file nothing reads, but it is worth someone deciding whether it should be deleted or made real.The default is unchanged. Leaving it on with a visible switch beside it is defensible and is what the issue scoped. Whether it should stay on is a product question about how users learn a release exists, and I did not want to answer it as a side effect of drawing a checkbox — say the word and I will file it separately.
Where it lives, and why not in
SaveAlertSettingsThe checkbox sits directly under "Minimize to system tray", which puts it above
AlertsEnabledCheckBox— the master switch where Lite's alert block actually begins. Note that in Lite there is no "General" section: "Minimize to tray" is the first child of the Notifications border, sitting above that gate precisely because it is not an alert setting. The update announcement is itself a tray balloon (AnnounceUpdate→_trayService.ShowNotification), so the two tray-behavior switches reading together at the top of that panel is the placement that made sense.Persistence deliberately does not go through
SaveAlertSettings, even thoughMinimizeToTraynext to it does. Being in the alert block would mean two wrong things: the control would gray out inUpdateAlertControlStateswith the "Enable notifications" master switch, claiming a dependency the update check does not have, andRestoreAlertDefaultsButton_Clickwould stomp it. So it takes the shape the Dashboard Defaults knobs already use —LoadCheckForUpdatesseeded from the static in the constructor,SaveCheckForUpdatescalled from the Save button, and oneApp.WriteSettingmerge into settings.json.The key name is unchanged, so anyone who already hand-edited it keeps their setting, and
WriteSettingparses the existing document and mutates it rather than rewriting, so nothing else in the file moves.The drift guard
AlertSettingsControlWiringTests.LiteSaveAlertSettings_PersistsEveryStaticItAssignsasserts everyApp.X =insideSaveAlertSettingshas a matchingroot[...] = App.Xin the same body. Putting the assignment in its own method leaves it outside that fact's scope, which is the correct answer rather than a dodge — the guard's subject is the alert block. The siblingEveryLoadedThresholdBox_IsAlsoGatedAndResetmatches\bAlert\w*Box\band explicitly drops anything ending inCheckBox, so it never reaches this control either.Lite.Teststargetsnet10.0-windowsand cannot run on macOS, so I reimplemented both facts in Python against the patched source — the same brace-matching method-body extraction and the same regexes — plus assertions on the new wiring itself. Both existing facts pass unchanged for Lite and for Darling (22 and 28 loaded threshold boxes, none ungated, none unreset; 56 assigned statics, all persisted),CheckForUpdatesOnStartupis confirmed absent fromSaveAlertSettings's body, the control is confirmed absent from both the gate and the reset methods, and the XAML control is confirmed to sit betweenMinimizeToTrayCheckBoxandAlertsEnabledCheckBox. CI is still the arbiter for the real run.I did not add a new test. The risk this change carries — loaded but never saved — is shared by all five existing single-value
Save*methods and none of them has a guard, so a pin for mine alone would be arbitrary; a general "everyLoad*in the constructor has aSave*in the Save button" check would need an exception list forLoadServerScheduleSummaryand a special case forSaveMcpSettingsAsync, which is the hand-maintained-list smell. Happy to take it as a follow-up if you want it.Darling
No equivalent, confirmed. The Darling Viewer's only Velopack use is in
AboutWindow, on demand when the user opens it; there is no startup check and no setting to match wording with. Lite'sAboutWindowdoes the same on-demand check, and I left it unconditional on purpose — opening About is asking, and this setting governs the call the user did not ask for. The tooltip says so, so a user turning the switch off is told where the deliberate check still lives; see the comment below for why it names the sidebar button rather than the "Help > About" the rest of the product promises.Verification
Built
Lite/PerformanceMonitorLite.csprojandLite.Tests/Lite.Tests.csprojon macOS withEnableWindowsTargeting, both clean. The first attempt failed onMC3000because the XAML comment contained--, which XML forbids; reworded.global.jsonwas restored and is not in the diff.