Repository navigation
fix(config): make vfox config write only the key it sets - #739
Open
linghengqian wants to merge 1 commit into
Open
linghengqian wants to merge 1 commit into
linghengqian wants to merge 1 commit into
Conversation
`vfox config <key> <value>` loaded the merged shared and user configuration, changed one field and saved all of it into the user's config.yaml. The user's file then held a copy of every section, the shared aliases included, and because a section that the user's file declares replaces the shared one, a later change of the shared configuration no longer applied. `--unset` did not remove the key either: it wrote the default value. Edit the user's file as a YAML document instead, and touch only the key that is set or unset. File is the generalization of AliasFile, which is now a view of the `aliases` key of a File, so `vfox alias` is unchanged. - Set checks the key against the yaml names of Config and the value against the type of the setting, and writes the value as that type. The other keys and the comments stay, a section the file does not declare stays undeclared, and a replaced value keeps the comment after it. A key that does not exist, a section, a list and a table are refused, and so is a boolean that is neither true nor false (it used to become false without a word). - Unset removes the key and the sections it leaves empty: a section without a key would still be one the file declares, and it would replace the shared one. A key that the file does not have is not an error, and the file is not rewritten. - Save refuses to write a file that does not load as a Config, so a change can no longer leave a config.yaml that stops every command (a cache duration under a second used to be written as ""). - A section that a file declares with one key keeps the defaults of the others when they are not zero values. Path and Diagnostics already did; LegacyVersionFile did not, so `vfox config legacyVersionFile.strategy x` would have switched the parsing of legacy version files off. Tests: with a shared config present, setting one key copies nothing else (on the config level and through the CLI), `--unset` hands the shared value back, and a test walks every setting of Config so that a new one of a type `vfox config` cannot set fails it. The old test of configSet moves to the new API with all its assertions. `vfox config` runs inside the test process now: it no longer changes the defaults that every command shares, which is why it had a process of its own. Part of version-fox#737.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #739 +/- ##
==========================================
+ Coverage 51.39% 52.03% +0.64%
==========================================
Files 111 114 +3
Lines 8240 8319 +79
==========================================
+ Hits 4235 4329 +94
+ Misses 3583 3566 -17
- Partials 422 424 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #737 (item 4, "
vfox configshould write only the key it sets").What changes
vfox config <key> <value>loaded the merged shared and user configuration and saved all of it into the user'sconfig.yaml;--unsetwrote the default value. Now the command edits the user's file as a YAML document and touches only the key that is set or unset, keeping the other keys and the comments.config.AliasFileis generalized intoconfig.Filefor it;AliasFileis now a view of thealiaseskey of aFile, sovfox aliasbehaves as before and its tests pass as they were; only the test of the top-level mapping moved toFile, together with the code it tests.Checked with real binaries, built from
mainand from this branch. The sharedconfig.yamlhaspath.mode: baseline,diagnostics.onUse: false,cache.availableHookDuration: 24hand an alias; the user is new and runsvfox config proxy.url http://localhost:7890:mainpath,diagnosticsandaliasesin the user's filepath.modetoaggressivebaselineaggressivevfox config --unset proxy.urlurl: ""stays in the filebaseline;config path.mode legacy, then--unset path.modelegacystays in the file and winspathis gone, the user getsbaselineagainCode
File.Setchecks the key against the yaml names ofConfigand the value against the type of the setting, and writes the value as that type. The other keys and the comments stay, a section the file does not declare stays undeclared, and a replaced value keeps the comment after it. Strings that YAML would read as something else ("true","21") are quoted.File.Unsetremoves the key and the sections it leaves empty. That part matters: a section without a key is still a section the file declares, and it would replace the shared one. A key the file does not have is not an error, and the file is not rewritten. A whole section can be unset (vfox config --unset path), which also cleans up the copies that earlier versions wrote.File.Saverefuses to write a file that does not load as aConfig. Before,vfox config cache.availableHookDuration 500mswroteavailableHookDuration: "", and aconfig.yamlthat does not load stops every command, including the one that could fix it.LegacyVersionFilegets theUnmarshalYAMLthatPathandDiagnosticshave, so a section declared with one key keeps the defaults of the others. Without itvfox config legacyVersionFile.strategy latest_installedon a file that lacks the section would have switched legacy version files off:enableistrueby default andfalseonce the section exists without it.AGENTS.mdsays that a section like this has to do the same.decodeConfigis whatNewConfigWithPathdid to read a file, extracted so thatSavecan check with the same code.Behavior that changes with it, all of them cases that used to go wrong without a word:
proxy.ulr) is an error. Before, nothing was set and the whole file was rewritten.truenorfalse(yes) is an error. Before, it becamefalse.proxy), a list or a table (path.preserve,diagnostics.ignore,aliases) is refused with a message. Lists and tables can still be edited in the file, and removed with--unset;aliaseshasvfox alias.cache.availableHookDurationother than-1, and one under a second, is refused. The messages for the HTTP timeouts name the key (plugin.http.downloadTimeout: must not be negative; it saidtimeoutfor both).Tests
internal/config:Fileagainst files with comments, no content, only a comment, a null document,{}, an empty or null section and the flow style; every type of setting; everything that has to be refused (the file stays as it was);Unsetstep by step through a file, including the sections that go with their last key; the shared-config scenario of Follow-ups of #736: plugin updates, the path.mode default and vfox config #737 (the file vfox creates on its first run, one setting changed, the shared config changed afterwards, the user's own value set and unset);Saverefusing a file that would not load; and a test that walks every setting ofConfig, so that a new setting of a typevfox configcannot set fails it.cmd/commands: with a shared config present, setting one key copies nothing else (a user with a file and a new user);--unsethands the shared value back; the errors leave the file alone. The oldTestConfigSetHTTPTimeoutmoved to the new API with all its assertions (TestSetHTTPTimeouts), and now also checks that unsetting removes the keys.configruns inside the test process now (inProcessincli_test.go). It had a process of its own because the oldconfigSetchanged the defaults that every command shares (EmptyPathand the like):TestConfigCommandHandlesTheNewSettingsfails onmain'sconfig.goonceconfigis ininProcess. The new code does not touch them.main'sconfig.go. I broke the implementation ten ways (no pruning of empty sections, the comment of a replaced value dropped, theSavecheck removed, the flow style kept for the first key, a null section not turned into a mapping,LegacyVersionFilewithout its defaults, the boolean error ignored, a negative timeout accepted, unknown keys accepted, a float setting without a parser) and a test failed each time.Docs
docs/guides/configuration.mdand the zh-hans copy: whatvfox config <key> <value>and--unsetdo to the file, that lists and tables are edited in the file, and a callout thatpathanddiagnosticsstill replace the shared section as a whole once the file declares them (sovfox config path.mode baselinedrops apath.preserveof the shared config for that user, andvfox config --unset pathgives it back).AGENTS.mdsays where this lives and replaces the sentence thatvfox configsaves the merged config.Not in this PR
pathanddiagnosticskey by key would need to know which keys a file declared; the callout above documents the rule as it is.proxy,gitignoreorlegacyVersionFile, the shared values of those sections do not apply.NewConfigWithPathputs the default in the missing section, andmergeProxy,mergeGitignoreandmergeLegacyVersionFiletake any non-nil user section as the user's. The other sections follow the shared config (path,diagnosticsandaliasesby design,plugin.httpper key,registry,storageandcachethrough the empty or default value). So--unsethands the shared value back for those, and the default for these three. I left the merge alone; it deserves its own issue.Config.SaveConfigis only used by tests now. It stays, with a comment that it is not how the user's file is changed; removing it touches five unrelated test files.path.preserve,diagnostics.ignore) from the command line.Draft for the release notes
Verification
go teston all packages exceptinternal/shared/printer(it needs a TTY): pass, on the commit as it is and on the tree that merges it with #738 (they do not conflict).go vetand the compilation of the tests forGOOS=linux,windowsanddarwin: clean. The docs site builds (VitePress 1.0.0 as inpackage-lock.json, no dead links). The CLI tests skip on Windows, as the fixture does (#737, item 5); theinternal/configtests run there.CI on my fork for the head commit (d4ebc1e) is green, with the Go 1.24.0 of
go.mod:VersionFox CI(ubuntu, macOS, Windows, the Windows regressions, Fish) andvfox E2E Test(Unix, macOS, Windows, MSIX packaging): CI, E2E.