Conversation
PR #1 discovers addressbooks via DAV/CardDAV namespaces, then only keeps members whose getcontenttype contains "vcard". iCloud omits that property and names contacts *.vcf, so listing returned zero URLs. Treat a vcard MIME type or a .vcf href as a contact, and skip collection hrefs that end with a slash.
Build on push to main and fix/carddav-icloud-vcf, push to ghcr.io/hubeight/bdaysync. Do not tag latest.
The previous commit replaced the repo workflow that was already running on this branch via workflow_dispatch.
Trivy failed because it used HubEight (must be lowercase) and fix/carddav-icloud-vcf (slash is invalid in a Docker tag). Use the same sanitized tag docker/metadata-action publishes.
iCloud lookups occasionally fail with ENETUNREACH (IPv6 without a route). Retry the GET up to three times with a short backoff so one blip does not drop a contact for the whole run.
After a complete CardDAV fetch, remove calendar events whose UID
matches birthday-{slug}-{YYYYMMDD} if that slug is not in the current
set of contacts that still have a birthday. Skip delete when any vCard
GET failed or the calendar listing is empty.
The Trivy image-ref fix is for this fork's GHCR job (HubEight vs hubeight, slashes in branch tags). It does not belong in a PR to anatosun/bdaysync.
fetch_complete compared fetched vCards with listed ones, so a failed addressbook listing (HTTP error, exception, broken XML) was never counted and still yielded complete=True. Unparseable birthdays were counted as fetched, and an empty contact list with a complete fetch deleted every birthday event. - Clear fetch_complete on any listing, download or parse failure; _parse_vcard now raises on unreadable data and returns None only when there is no BDAY. Listing PROPFIND gets a timeout. - Restore main_sync returning False when no contacts were found, so orphan delete never runs with an empty set. - Match orphans on name slug plus month/day, so a changed birthday replaces the old event instead of leaving a duplicate. - Share the UID slug between event creation, lookup and orphan delete. - The retry docstring blamed IPv6: urllib3 tries every resolved address and IPv4 sorts first, so ENETUNREACH is only the last error. Add test_sync.py covering each case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Orphan delete removes calendar data, so it is opt-in. Wired like the other BIRTHDAY_* settings: config, .env.template, docker-compose and README, and logged with the event configuration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
birthday.replace(year=year) raised for 29 February in common years, so the event was never created or updated there. Created in a leap year, plain FREQ=YEARLY only recurs every four years (RFC 5545 skips invalid dates). Such birthdays now fall on 28 February in common years with FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1. The UID keeps month/day from the contact, so orphan delete still matches it. Normal UIDs are unchanged. Checked against iCloud: the rule is accepted, the time-range lookup finds the event on 2027-02-28 and 2028-02-29, and a second run creates no duplicate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is based on #2 (which is based on #1). Only the last commit is new here; the other commits come from #2 and will drop out of the diff once it is merged.
Birthdays on 29 February were broken in two ways:
birthday.replace(year=year)raises "day is out of range for month", so the event is neither created nor updated. With the default run (current and next year) that means no event at all in three out of four years.FREQ=YEARLY. RFC 5545 skips invalid dates, so it only shows up every four years (2028, 2032, 2036).Such birthdays now fall on 28 February in common years and on 29 February in leap years, using
RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=-1. The UID keeps month/day from the contact (...0229), so the orphan cleanup from #2 still matches the event. UIDs of all other birthdays are unchanged.I checked it against iCloud: the rule is accepted, the time-range lookup finds the event on 2027-02-28 and 2028-02-29 (not on 2028-02-28), and a second run creates no duplicate.
bdaysync/test_sync.pyhas three new tests for this (cd bdaysync && python -m unittest test_sync).🤖 Generated with Claude Code