Repository navigation
fix(no-ticket): stop credential helpers from overwriting config files that do not parse - #540
Open
cloudsmith-iduffy wants to merge 2 commits into
Open
cloudsmith-iduffy wants to merge 2 commits into
cloudsmith-iduffy wants to merge 2 commits into
Conversation
… that do not parse merge_config_file read a config.json or config.toml with a syntax error as an empty object and wrote the result over the file. The next write replaced the .bak, so the original content was lost. Now it raises ConfigFileParseError, which names the file, and writes nothing. The install and uninstall commands show this error and exit with code 1. Change detection now compares the data before and after mutate, not the serialised text. A file that is valid but has a different formatting is no longer rewritten when mutate makes no change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
Static review identified no blocking issues in the focused parsing safeguards, change detection, CLI handling, or regression coverage; automated tests were not run.
No open issues 🎉
What changed in this PR
Protects Docker and Cargo configuration files from being overwritten when parsing fails, and avoids unnecessary rewrites when credential-helper operations make no data changes.
Changes:
- Rejects malformed, non-UTF-8, and non-object configurations while supporting TOML files with a UTF-8 BOM.
- Compares configuration data before and after mutation to preserve unchanged files and avoid unnecessary backups.
- Adds clear CLI errors, regression tests, and changelog entries.
| File | Description |
|---|---|
cloudsmith_cli/core/tests/test_cache_utils.py |
Tests parsing safeguards, BOM handling, and no-op preservation. |
cloudsmith_cli/core/cache_utils.py |
Adds strict parsing and data-based change detection. |
cloudsmith_cli/cli/tests/commands/test_credential_helper_install.py |
Tests install/uninstall errors and malformed-file preservation. |
cloudsmith_cli/cli/commands/credential_helper/manage.py |
Converts configuration parsing failures into CLI errors. |
CHANGELOG.md |
Documents file-preservation and no-op behavior fixes. |
Review effort: Balanced
cloudsmith-iduffy
marked this pull request as ready for review
October 24, 2026 17:42
cloudsmith-iduffy
enabled auto-merge (squash)
October 24, 2026 17:42
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.
Description
credential-helper installanduninstalledit the Dockerconfig.jsonand the Cargoconfig.tomlthroughmerge_config_file(). That function had two problems.A file with a syntax error got overwritten. If the file did not parse, the function used
{}and wrote the result over the file. The.bakfile was replaced on the next write, so the original content was lost for good. One trailing comma inconfig.jsonwas enough:Before:
authsandcredsStoreare gone. For Cargo,uninstallon aconfig.tomlwith an unclosed array emptied the file to 0 bytes. It also reported that it removed entries, and it exited with code 0.After:
The file stays as it was, and the command makes no
.bak. A file that is not UTF-8, or whose top-level value is not an object (for example a JSON list), gives the same kind of error. A missing file, an empty file, or a file that has only whitespace still counts as{}.A valid file was rewritten when nothing changed. The function compared the new serialised text with the text on disk. Docker writes
config.jsonwith tabs, so the text never matched. A no-opuninstall dockerrewrote the file, made a.bak, and printed "removed credHelpers entries". Now the function makes a deep copy of the data beforemutateand compares the data, not the text. If the data does not change, it does not touch the file and returnsFalse. This also applies to a missing file: a no-op merge no longer creates it.Changes:
core/cache_utils.py: newConfigFileParseError(ValueError)and a_parse_config_text()helper. The function now detects changes on the data, has stricter type hints, and strips a UTF-8 BOM before it parses TOML (Cargo accepts a BOM, tomlkit does not).cli/commands/credential_helper/manage.py:installanduninstallturnConfigFileParseErrorinto aClickException, so the user sees one clear error line and exit code 1.Type of Change
Additional Notes
What changes for users and scripts: if
config.jsonorconfig.tomldoes not parse,credential-helper installanduninstallnow exit with code 1 instead of 0. They print an error that names the file, and they do not write to it. Previously, the command silently replaced the file. A script that raninstallagainst a broken config now fails, so the user must correct or remove the file. Also, a no-op run no longer rewrites a correctly formatted file, so the file keeps its formatting and the command no longer writes a.bak.Known gap, left for a follow-up: both installers write or remove the launcher before they merge the config. When the parse fails,
installhas already written the launcher, anduninstallhas already removed it. The config file stays safe, and a second run after the fix finishes the job. Moving the merge before the launcher step means changes tocredential_helpers/docker/installer.py, which #522 also changes, so I left it out of this PR.This PR overlaps with #522: with data-based change detection,
uninstall dockerno longer createsconfig.jsonwhen the file is missing. Theconfig_existsguards in #522 and in the Cargo installer are still correct, so they can stay.How to check it:
New tests: an unparsable JSON file, a top-level JSON list, an unparsable TOML file, a file that is not UTF-8, a TOML file with a BOM, an empty or whitespace-only file, a tab-indented no-op, an empty-file no-op, a missing-file no-op, and a CLI test for
installanduninstall. The CLI test checks exit code 1, the file path in the message, an unchanged file, and no.bak.🤖 Generated with Claude Code