[core] Use native stored ZIPs for support bundles and profile backups#3206
Conversation
Co-authored-by: Codex <noreply@openai.com>
There was a problem hiding this comment.
Thanks @jensenpat — clean extraction. The ZipArchive helper is a straight lift-and-shift of the existing reader/writer with two small upgrades (explicit numeric_limits overflow guards for entry count / sizes / offsets, and static_cast<uInt> on the crc32 size argument), and the support-bundle path drops the QProcess/powershell/tar shell-out in favor of in-process ZIP writing via QSaveFile. Nice removal of an external-process dependency — and QSaveFile::commit() gives atomic replace semantics the old shell-out couldn't.
A few small notes for your consideration, none blockers:
1. Linux/macOS support bundle file extension change (worth a CHANGELOG entry)
src/core/SupportBundle.cpp:202 previously produced support-bundle-*.tar.gz on non-Windows and .zip on Windows. After this PR, all platforms produce .zip. I grepped the tree and found no consumer of support-bundle-*.tar.gz (the only hit was the line being removed), so this is safe. Worth a brief note in CHANGELOG.md since existing Linux/macOS users with scripts or muscle memory expecting .tar.gz will be surprised.
2. collectSupportBundleEntries is flat-only by design
QDir::Files + no QDirIterator means subdirectories under the temp bundle dir are silently dropped. Today SupportBundle::createBundle writes only top-level files (logs, JSONs, settings.xml, enabled-categories.txt — all flat), so this matches current behavior. Worth a one-line comment in collectSupportBundleEntries noting the flat-only invariant so a future contributor who adds, say, a crashdumps/ subdir doesn't end up with a silently-empty section in the bundle.
3. Stored-only ZIP and uncompressed log size
The shared helper only writes stored (method 0) ZIPs, so a support bundle now uncompresses to roughly the sum of input file sizes plus headers, where the old tar czf deflated logs. AetherSDR logs are rotated small so this is unlikely to be a real problem in practice, but if a user has logs in the tens of MB the bundle is now noticeably larger. The PR title is explicit ("native stored ZIPs") so this is presumably intentional — flagging in case it's worth a follow-up to wire deflate into writeStoredZip later (the reader already handles method 8).
4. uncompressedSize is dead in the stored-method branch
src/core/ZipArchive.cpp:142 reads uncompressedSize but it's only consumed inside the method == 8 branch. Pre-existing — moved verbatim from ProfileTransfer.cpp — but since you're touching this code, a [[maybe_unused]] or restructuring the read to live inside the deflate branch would be cleaner. Skip if you'd rather keep the lift truly verbatim.
Otherwise this looks good — the round-trip + corruption test in tests/zip_archive_test.cpp covers the contract well, and routing ProfileTransfer through the same helper means the existing profile_transfer_test exercises the writer too. Thanks again.
🤖 aethersdr-agent · cost: $8.6056 · model: claude-opus-4-7
|
Claude here — merged, thanks @jensenpat. Clean refactor + the WACK "Blocked executables" finding is gone. The null-safe error setter and Filed #3218 to track adding deflate compression as a follow-up. Confirmed nothing in Store policy or WACK objects to runtime deflate — the rule was about subprocess imports, which this PR resolved. Switching back to compressed bundles is purely a UX win for users hitting Discord/email size limits. 73, Jeremy KK7GWY & Claude (AI dev partner) |
## Summary Closes #3218. Support bundles are log-dominated and compress ~5–10×, but the in-process ZIP writer from #3206 only emitted **stored** (uncompressed) entries — a user with 50 MB of logs produced a 50 MB bundle, hitting Discord/email/GitHub attachment limits much sooner. Adds a **raw-DEFLATE** writer using the **same bundled zlib** that already powers the inflate-on-read path (negative window bits → no zlib header, which ZIP doesn't use). No new dependency, no subprocess, no new MSIX content — **all of #3206's Store-compliance wins are preserved**. ## Changes - `ZipArchive`: `writeStoredZip` and the new `writeDeflatedZip` now share one `writeZipImpl(entries, compress)` — method field + independent compressed/uncompressed sizes; CRC is always over the **uncompressed** data. - `SupportBundle::writeSupportBundleZip` → `writeDeflatedZip`. `ProfileTransfer` stays stored (tiny payloads). - `zip_archive_test`: new deflated round-trip asserting method-8 framing, smaller-than-raw output, and a clean round-trip **through the existing reader** (writer/reader cross-check, since `readZipEntries` already handles method 8). ## Why it's not RFC-gated Internal storage-format change with no UX/behavior change (bundles are still ZIPs, just smaller); the file is never inside the MSIX so WACK's archive rules don't apply. ## Test plan - [x] `zip_archive_test` passes (stored + deflated round-trips, method byte, size check) - [x] Full app builds clean 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…aethersdr#3206) ## Summary - Extract the profile transfer ZIP reader/writer into a shared `ZipArchive` helper. - Write support bundles as native uncompressed `.zip` files using the shared stored-ZIP writer instead of launching PowerShell `Compress-Archive` or `tar`. - Add a focused ZIP round-trip/corruption test for the shared helper. ## Validation - Configured and built with MSVC via `scripts/enter-msvc.ps1 -Arch x64` and Ninja using 8 jobs. - Built `build_opus`, `zip_archive_test`, `profile_transfer_test`, and `AetherSDR`. - Ran `profile_transfer_test` and `zip_archive_test` separately with Qt on `PATH`; both passed. - Ran `windeployqt` on the built app for manual testing. - Scanned the built executable/deployed executable: no `powershell`, `Compress-Archive`, or `.tar.gz` support-bundle path remains. - Checked direct process-launch imports with `dumpbin`; no direct `CreateProcess`/`ShellExecute` import was found. Co-authored-by: Codex <noreply@openai.com>
Summary
ZipArchivehelper..zipfiles using the shared stored-ZIP writer instead of launching PowerShellCompress-Archiveortar.Validation
scripts/enter-msvc.ps1 -Arch x64and Ninja using 8 jobs.build_opus,zip_archive_test,profile_transfer_test, andAetherSDR.profile_transfer_testandzip_archive_testseparately with Qt onPATH; both passed.windeployqton the built app for manual testing.powershell,Compress-Archive, or.tar.gzsupport-bundle path remains.dumpbin; no directCreateProcess/ShellExecuteimport was found.