Skip to content

fix: make _safe_write_json actually atomic with mkstemp + os.replace - #3971

Open
Quratulain-bilal wants to merge 1 commit into
github:mainfrom
Quratulain-bilal:fix/events-safe-write-json-atomic
Open

fix: make _safe_write_json actually atomic with mkstemp + os.replace#3971
Quratulain-bilal wants to merge 1 commit into
github:mainfrom
Quratulain-bilal:fix/events-safe-write-json-atomic

Conversation

@Quratulain-bilal

Copy link
Copy Markdown
Contributor

Problem

Despite its name, _safe_write_json used write_text() which truncates the file before writing. A crash or power loss mid-write leaves a partial JSON file. This function is called by _merge_copilot_json, _merge_opencode_plugin_ref, etc., meaning all Copilot and OpenCode event config writes were non-atomic.

Fix

Now uses empfile.mkstemp + os.replace for atomic writes, matching the pattern used in _utils.py, shared_infra.py, and other safe-write utilities in the codebase.

Despite its name, _safe_write_json used write_text() which truncates the
file before writing. A crash or power loss mid-write leaves a partial
JSON file. Now uses tempfile.mkstemp + os.replace for atomic writes,
matching the pattern used in _utils.py, shared_infra.py, and other
safe-write utilities in the codebase.

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

Makes event configuration JSON writes atomic using a temporary file and os.replace.

Changes:

  • Writes JSON to a same-directory temporary file.
  • Cleans up temporary files on failure.
Show a summary per file
File Description
src/specify_cli/events.py Adds atomic JSON replacement logic.

Review details

馃挕 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Suppressed comments (1)

src/specify_cli/events.py:2024

  • mkstemp creates the staged file with mode 0600, and os.replace swaps that inode over the existing config. Unlike the previous write_text call, this silently changes permissions (and potentially ownership) on every merge, which can lock other users or processes out of shared 0640/0644 config files. Preserve the existing file metadata before replacing it, as the atomic writer in src/specify_cli/_utils.py:163-193 does.
        os.replace(tmp, dst)
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/specify_cli/events.py
with os.fdopen(fd, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2)
f.write("\n")
os.replace(tmp, dst)

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please address Copilot feedback

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.

3 participants