f625ecc263
Fixes: - Sandbox: add LaunchAgent temporary-exception entitlement - Mirror URL: fix broken buildMirrorURL (URLComponents with ; separator) - Cleanup: add bounds check to prevent crash on missing source - State safety: pass MirrorConfig instead of mutating global @State - KVC: remove misleading do-catch around setValue:forKey: - Log cap: limit in-memory log to 2000 lines - CLI: fix race with calendar loading - launchCtl: separate stdout/stderr pipes Features: - Cancel button for long-running mirrors - Progress indicator for multi-route runs (Route X of Y) - Target event cache across routes Code quality: - Extract BlockMath, MirrorUtils, EventFilters, MirrorConfig - Add 45 unit tests across 3 test files - Refactor mergeGapMin to computed property - Make log editor read-only Build: - Bump version to 1.4.0 (build 18) - Add LSMinimumSystemVersion 15.5
91 lines
6.3 KiB
Markdown
91 lines
6.3 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to BusyMirror will be documented in this file.
|
||
|
||
## [1.4.0] - 2026-05-27
|
||
|
||
### Fixed
|
||
- **Sandbox LaunchAgent**: added `temporary-exception` entitlement so scheduled runs work in the sandboxed app.
|
||
- **Mirror URL generation**: `buildMirrorURL` was silently broken — `URL(string:)` rejects raw `|` characters on current macOS, so mirror metadata URLs were always `nil`. Rebuilt with `URLComponents` using `;` separator and backward-compatible parser.
|
||
- **Crash on Cleanup**: `runCleanup()` no longer crashes if the selected source calendar was removed.
|
||
- **State corruption in multi-route runs**: `runConfiguredRoutes` no longer mutates global `@State` settings and restores them at the end of each loop; instead it passes a `MirrorConfig` struct into the engine.
|
||
- **KVC safety**: removed misleading `do-catch` around `setValue:forKey:` in `setEventPrivateIfSupported()` (Objective-C exceptions are uncatchable in Swift).
|
||
- **Log memory leak**: in-memory log now caps at 2,000 lines.
|
||
- **CLI race**: `tryRunCLIIfPresent()` now preloads calendars when access is already granted, eliminating the 10-second timeout race.
|
||
- **launchCtl output**: stdout and stderr now use separate pipes instead of interleaving into one.
|
||
|
||
### Added
|
||
- **Cancel button**: long-running mirrors now show a Cancel button; loops check `Task.isCancelled` for responsive cancellation.
|
||
- **Progress indicator**: multi-route runs display `"Route X of Y"` in the status area.
|
||
- **Unit tests**: 45 tests across `BlockMathTests`, `MirrorUtilsTests`, and `EventFiltersTests`.
|
||
- **Extracted modules**: `BlockMath.swift`, `MirrorUtils.swift`, `EventFilters.swift`, and `MirrorConfig.swift` separate pure logic from the UI monolith.
|
||
- **Target event cache**: target calendars shared across routes are fetched only once per run session.
|
||
|
||
### Changed
|
||
- `mergeGapMin` is now a computed property instead of redundant `@State`.
|
||
- Log editor is now read-only (still selectable/copyable).
|
||
- `SettingsPayload.excludedOrganizerFilters` is now non-optional for consistency.
|
||
|
||
### Build
|
||
- Bump minimum macOS version to `15.5` in `Info.plist`.
|
||
- Bump version to **1.4.0** (build **18**).
|
||
|
||
## [1.3.9] - 2026-04-09
|
||
- New: add a macOS menu bar extra with `Sync Now`, `Open BusyMirror`, and `Quit BusyMirror`.
|
||
- UX: menu bar sync requests reuse the existing mirror flow and can open the main window automatically when needed.
|
||
- UX: BusyMirror now runs as a menu bar-only app and no longer appears in the Dock.
|
||
- Build: bump version to 1.3.9 (build 17).
|
||
|
||
## [1.3.8] - 2026-04-08
|
||
- Fix: release ZIPs now package `BusyMirror.app` at the archive root instead of embedding the full build path.
|
||
- Fix: release builds now apply an ad-hoc bundle signature before packaging so downloaded artifacts pass `codesign --verify --deep --strict`.
|
||
- Build: suppress resource fork sidecars in release ZIPs via `ditto --norsrc --keepParent`.
|
||
- Build: bump version to 1.3.8 (build 16).
|
||
|
||
## [1.3.7] - 2026-03-24
|
||
- Fix: mirror reconciliation now survives target providers that strip BusyMirror's custom event URL metadata.
|
||
- Fix: moved and deleted source events are tracked via stable EventKit identifiers and a persisted local mirror index, so target placeholders update reliably.
|
||
- Fix: mirror updates now detect title and notes changes, not just start/end time changes.
|
||
- Build: bump version to 1.3.7 (build 15).
|
||
|
||
## [1.3.6] - 2026-03-13
|
||
- Scheduling: add in-app `Scheduled runs` controls to install or remove a user `launchd` LaunchAgent from BusyMirror itself.
|
||
- Scheduling: support `Hourly`, `Daily`, and `Weekdays` schedules; hourly mode runs saved routes via `StartInterval`.
|
||
- UX: generate and ship a proper macOS app icon set for BusyMirror.
|
||
- Build: bump version to 1.3.6 (build 14).
|
||
|
||
## [1.3.4] - 2026-03-13
|
||
- Fix: route-scoped cleanup no longer deletes placeholders created by other source routes during the same multi-route run.
|
||
- Fix: stale calendars are pruned from saved selections and routes during refresh, and refresh now recreates `EKEventStore` for a hard reload.
|
||
- UX: the top bar `DRY RUN` / `WRITE` status pill is clickable, the left column keeps its own height on desktop, and the app can reveal its log file from the UI.
|
||
- Logging: mirror activity is persisted to `~/Library/Logs/BusyMirror/BusyMirror.log` with simple rotation to `BusyMirror.previous.log`.
|
||
- CLI: add `--run-saved-routes` so scheduled `launchd` runs can use the saved UI routes instead of fragile index-based route definitions.
|
||
|
||
## [1.3.1] - 2025-10-13
|
||
- Fix: auto-delete of mirrored placeholders when the source is removed now works even if no source instances remain in the window. Also cleans legacy mirrors without URLs by matching exact times.
|
||
|
||
## [1.3.2] - 2025-10-13
|
||
- New: Organizer filters — skip events by organizer (name/email/URL). UI under Options and persisted in settings.
|
||
- CLI: add `--exclude-organizers` (and `--exclude-titles`) flags to control filters when running headless.
|
||
|
||
## [1.2.4] - 2025-10-10
|
||
- Fix: enable “Mirror Now” when Routes are defined even if no Source/Targets are checked in the main window. Button now enables if either routes exist or a manual selection is present.
|
||
|
||
## [1.3.0] - 2025-10-10
|
||
- New: Mark Private option to mirror with prefix + real title and set event privacy on supported servers; available globally and per-route; persisted.
|
||
- Misc: calendar access fixes, concurrency annotations, accepted‑only filter, settings autosave/restore, Mirror Now enablement.
|
||
|
||
## [1.2.3] - 2025-10-10
|
||
- Fix: reliably save and restore settings between runs via autosave of key options and restoration of source/target selections by persistent IDs.
|
||
- UX: persist Source and Target selections; rebuild indices on launch so UI matches saved IDs.
|
||
- Build: bump version to 1.2.3 (build 5).
|
||
|
||
## [1.2.1] - 2025-10-10
|
||
- Fix: reinitialize EKEventStore after permission grant to avoid “Loaded 0 calendars” right after approval.
|
||
- Fix: attendee status filter uses current user’s attendee `participantStatus == .accepted` instead of unavailable APIs.
|
||
- Concurrency: mark `requestAccess()` and `reloadCalendars()` as `@MainActor` to satisfy strict concurrency checks.
|
||
- Dev: add Makefile with `build-debug`, `build-release`, and `package` targets; produce versioned ZIP + SHA-256.
|
||
|
||
## [1.2.0] - 2024-09-29
|
||
- Feature: multi-route mirroring, overlap modes, merge gaps, work hours filter, CLI support, export/import settings.
|