Skip to content

fix(events): preserve a non-UTF-8 config.toml on hook install/teardown - #3963

Open
marcelsafin wants to merge 1 commit into
github:mainfrom
marcelsafin:fix/toml-hooks-decode
Open

fix(events): preserve a non-UTF-8 config.toml on hook install/teardown#3963
marcelsafin wants to merge 1 commit into
github:mainfrom
marcelsafin:fix/toml-hooks-decode

Conversation

@marcelsafin

Copy link
Copy Markdown
Contributor

Description

_merge_toml_fragment() and _remove_toml_entries() read the user's config.toml (Codex hooks) with bare read_text() calls, so a non-UTF-8 (or otherwise unreadable) file crashes install_integration_events() and remove_integration_events() with a raw UnicodeDecodeError — and the merge path regenerates the file from what it read, so it would have discarded the user's bytes had it not crashed first. Every JSON merge/remove path already goes through _load_user_json(), which skips on an unreadable file precisely to preserve user content (#22).

Fix:

  • merge: abort and return False so the caller skips tracking the untouched file (same S5 contract as _merge_copilot_json), leaving the user's bytes intact;
  • teardown: skip the cleanup with a warning instead of crashing — the file contains only user content as far as we can tell, and the caller drops the manifest claim either way (S9).

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest (6,311 passed, 176 skipped)
  • Two new regression tests (install with pre-existing non-UTF-8 config.toml, teardown after the config was rewritten as non-UTF-8; both fail on main, pass with fix)
  • ruff check src tests clean

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (describe below)

Implemented autonomously by GitHub Copilot CLI (model: Claude Fable 5) under human direction; TDD (failing test first), full suite and lint verified locally. Commit includes Assisted-by/Co-authored-by trailers.

_merge_toml_fragment() and _remove_toml_entries() read the user's
config.toml with bare read_text() calls, so a non-UTF-8 (or otherwise
unreadable) file crashed install_integration_events() and
remove_integration_events() with a raw UnicodeDecodeError — and the
merge path regenerates the file from what it read, so it would have
discarded the user's bytes had it not crashed first. Every JSON
merge/remove path already goes through _load_user_json(), which skips
on an unreadable file to preserve user content (github#22).

Abort the merge (returning False so the caller skips tracking, S5) and
skip the teardown cleanup with a warning, leaving the user's bytes
untouched in both directions.

Assisted-by: GitHub Copilot (model: claude-fable-5, autonomous)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 3, 2026 19:56
@marcelsafin
marcelsafin requested a review from mnriem as a code owner August 3, 2026 19:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents Codex hook installation and teardown from corrupting or crashing on unreadable/non-UTF-8 user configuration.

Changes:

  • Safely skips TOML merge and cleanup when decoding fails.
  • Avoids manifest tracking for skipped merges.
  • Adds install and teardown regression tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/specify_cli/events.py Handles unreadable TOML while preserving user content.
tests/integrations/test_events.py Tests byte preservation during install and teardown.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants