Skip to content

Add safe macOS archive migration and native windows - #103

Open
LeonidasZhak wants to merge 1 commit into
blackboxo:masterfrom
LeonidasZhak:codex/macos-icloud-archive
Open

Add safe macOS archive migration and native windows#103
LeonidasZhak wants to merge 1 commit into
blackboxo:masterfrom
LeonidasZhak:codex/macos-icloud-archive

Conversation

@LeonidasZhak

Copy link
Copy Markdown

Summary

  • add a resumable macOS archive workflow that organizes cleanup candidates by year/month, account, conversation, and file type
  • deduplicate content with SHA-256 and keep one verified canonical archive copy
  • replace the per-file whole-JSON rewrite with item-level SQLite transactions
  • split archiving and source cleanup into two explicit UI actions; archive batches never remove WeChat source files
  • restore native macOS title bars and the standard close/minimize/zoom controls
  • apply the existing macOS entitlements during PyInstaller builds and disable UPX for the app bundle

Why

The previous frameless window flag removed normal macOS window controls. The first archive prototype also persisted a growing JSON manifest after every item. At tens of thousands of records this creates substantial write amplification, and an out-of-space error can interrupt the state transition around cleanup.

This PR moves state to SQLite/WAL with FULL synchronous transactions. Each item records archiving and removing before the external file operation, so an interrupted run can be reconciled conservatively on restart.

Archive and cleanup safety model

  1. Archive: scan the existing cleanup candidates, hash content, copy through a temporary file, fsync, verify SHA-256, and organize one bounded batch. Source files remain unchanged.
  2. Upload check: for iCloud Drive targets, query macOS Foundation ubiquitous-item status. Unknown, uploading, or errored states retain the source.
  3. Cleanup: expose a separate confirmation page. The user must confirm that the archive opens in Finder; one run processes at most 200 files / 512 MB and uses send2trash.

Additional guards:

  • the default and migrated configuration cannot auto-remove archived sources
  • each cleanup rechecks the archive identity marker, archived-file hash, and current source hash
  • a missing/mismatched archive, changed source, symlink, or hash collision blocks the item
  • copying requires the source size plus a 512 MB free-space reserve; cleanup requires 64 MB for local state writes
  • archive, upload-check, and removal work are item-bounded to avoid unbounded zero-byte or waiting queues
  • legacy JSON state is preserved and imported read-only; old ready_to_remove entries require the new verification path before cleanup
  • no message database is read or decrypted; paths without a conversation identifier use unknown_conversation

User-visible changes

  • new 归档与迁移 button and a three-step archive/verify/clean dialog
  • separate cleanup confirmation with eligible item/byte counts and a Finder shortcut
  • native macOS window controls; the redundant in-content exit control is hidden on macOS
  • clearer progress and completion messages that distinguish copied, duplicate, waiting, ready, retained, failed, and unconfirmed items

macOS archive workflow

Verification

  • python -m unittest discover -s tests -v — 31 tests passed
    • deduplication, filename collisions, protected extensions, batching, resume, iCloud waiting/uploaded transitions
    • explicit cleanup authorization, per-run bounds, source mutation, missing/mismatched archive identity, low disk space
    • interrupted archive/removal reconciliation, Trash adapter failures, legacy JSON import, and SQLite integrity
    • native macOS window flags, real archive button wiring, and separate archive/cleanup dialog actions
  • ruff check for the new archive module and tests; focused undefined-name/syntax lint for main.py
  • python -m py_compile main.py utils/archiveMigration.py utils/selectVersion.py
  • generated and indexed 75,000 temporary synthetic files:
    • 75,000 SQLite rows, PRAGMA integrity_check = ok
    • 11.385 s indexing time and 42,958,848-byte database on the test machine
  • separate 75,000-row automated database test verifies integrity, bounded database size, and absence of JSON .tmp rewrites
  • PyInstaller 6.21.0 macOS arm64 .app build with Python 3.13.12
  • codesign --verify --deep --strict passed; packaged entitlements were inspected
  • packaged app stayed running for a six-second isolated-HOME launch smoke with empty stderr and no crash traceback
  • git diff --check

Validation boundary

The final test pass did not upload files to a real iCloud account or touch a real WeChat directory. iCloud state transitions are covered with deterministic status fixtures, while the packaged app, Foundation bridge, signing, and isolated launch were validated locally.

Related context

This builds on the upstream macOS startup work in 3f4a429.

@LeonidasZhak
LeonidasZhak marked this pull request as ready for review July 29, 2026 11:40
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.

1 participant