• v0.4.0 c0b07c397f

    v0.4.0 — Read-only iCalendar subscriptions
    All checks were successful
    CI / gate (push) Successful in 1m1s
    CI / image (push) Successful in 51s
    Stable

    joeyr released this 2026-08-09 09:53:14 -05:00 | 110 commits to main since this release

    Added

    • Read-only iCalendar subscriptions (ICS_FEEDS), fetched over HTTPS and merged into
      the existing tools. A subscribed calendar is not stored in the iCloud account — every
      device polls the URL independently — so CalDAV never returns one and no CALENDARS
      value could ever reach it. That is why Apple's US Holidays was missing rather than
      filtered: the account only ever offered six collections and it was not among them.
      Feeds go through the same expand() as CalDAV data, so an occurrence from a
      subscription is indistinguishable downstream, including recurrence and all-day
      handling. Verified against Apple's real US_en.ics: 45 events in 2026, July 4 spanning
      exactly one day.

      Fetching the feed rather than importing it into a real calendar is the point — the file
      currently runs to Dec 2029, and an import would quietly stop returning holidays when it
      ran out, with nothing to say why.

      Failure modes here nearly all produce an empty calendar rather than an error, so each
      is made loud: an undecoded gzip body (iCloud serves gzip whether asked or not), an HTML
      error page served as 200, a body over 5 MB, a non-http(s) scheme (urlopen will read
      file:// quite happily), and a feed that is simply unreachable — which reports the
      calendar as unavailable rather than empty, and logs an advisory at boot.

      Not filtered by CALENDARS: configuring a feed is already the act of opting in, and the
      name is not knowable until after the first fetch.

    • A subscriptions preflight check, present only when feeds are configured. Advisory
      rather than fatal — a third-party URL being down is no reason to stop serving the
      account's own calendars.

    • The boot log now names the calendars the allowlist hid, not just how many.
      skipped_by_allowlist=2 answers "am I seeing everything?" with "no" and stops there,
      and the only other way to learn the names was to run once with CALENDARS empty —
      deliberately reading every subscribed collection on the account to find out which ones
      to exclude. A calendar created on the phone now shows up in this line.

    • A CALENDARS entry that matches nothing is now warned about individually. The
      existing warning only fired when every entry was wrong; one bad entry among several
      good ones started cleanly and quietly never consulted that calendar, which is
      indistinguishable from an empty calendar at the tool layer. The realistic cause is
      emoji — a warning sign is two codepoints (U+26A0 U+FE0F) and the bare U+26A0 renders
      identically without matching.

    Fixed

    • CI's gate job ran on Python 3.11 while the container runs 3.12, so nothing
      exercised the interpreter production actually uses. It runs on 3.12 now, and
      requires-python and mypy's python_version moved to 3.12 to match — a floor nothing
      tests is a claim nobody checks, and one version everywhere is simpler than two. The
      package is not published anywhere, so nothing downstream cares about the floor.
      Dropping the 3.11 leg also halves CI wall-clock: there is one shared runner, so matrix
      legs ran sequentially rather than in parallel.
    Downloads