<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/scripts, branch main</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-20T00:46:23+00:00</updated>
<entry>
<title>feat(calendar-sync): add Python helper for Google Calendar API sync</title>
<updated>2026-05-20T00:46:23+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-20T00:46:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d6a995b9090ca35190e59e765d5c14daf887e9d8'/>
<id>urn:sha1:d6a995b9090ca35190e59e765d5c14daf887e9d8</id>
<content type='text'>
Google's .ics export drops per-occurrence response statuses on recurring events. When OOO auto-declines a meeting, the master event keeps PARTSTAT=ACCEPTED and declined instances inherit it. The .ics path can't filter the declines out. The API path expands recurrences server-side via singleEvents=True, and each occurrence carries its own attendees[].self.responseStatus.

scripts/calendar_sync_api.py fetches events and renders them as org entries. OAuth is one-time per account. The refresh token lives at ~/.config/calendar-sync/token-&lt;account&gt;.json under 0600. Output matches the existing .ics shape: heading sanitization, LOCATION/ORGANIZER/STATUS/URL property drawer, HTML-stripped descriptions, org timestamps with weekday abbreviations.

I wrote 30 stdlib-unittest tests against fixture JSON, covering rendering, filtering, timestamp formatting, and HTML cleanup. I left auth and HTTP uncovered — they're thin wrappers around the Google client libraries, best checked by running the script once after OAuth setup.

docs/calendar-sync-api-setup.org walks through the Google Cloud OAuth client setup and the per-account auth bootstrap. .gitignore picks up Python bytecode now that the project has a Python helper.

The Elisp dispatch (:fetcher 'api routing in calendar-sync.el) lands in a follow-up commit.
</content>
</entry>
<entry>
<title>feat(coverage): report modules missing from SimpleCov + project-module score</title>
<updated>2026-05-15T07:13:10+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-15T07:13:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=4d8f979948d5349404a36fe335eb77955d068a8d'/>
<id>urn:sha1:4d8f979948d5349404a36fe335eb77955d068a8d</id>
<content type='text'>
=make coverage= used to print a line-weighted percentage that only saw
files SimpleCov instrumented.  104 modules existed on disk but only 49
appeared in =.coverage/simplecov.json=, so the headline number was
flattering: untouched modules counted for nothing.

The summary script now adds two things on top of the existing report:

- A =Not in SimpleCov report= section listing modules present under
  =modules/*.el= but absent from the SimpleCov output.  Missing-module
  detection is exactly direct =modules/*.el=; subdirectories and =.elc=
  files are ignored.
- A =Project module coverage= line that is module-weighted across every
  direct =modules/*.el= file.  Tracked modules contribute their per-file
  coverage percentage; missing modules contribute 0%.

The original line-weighted SimpleCov percentage stays as the
=instrumented coverage= number.  The new module-weighted score is the
honest project-level reading: missing modules count as 0% without
inventing a fake executable-line denominator for them.

Tests assert the missing-module section, the new percentage, and the
ignore rules for .elc / nested files.
</content>
</entry>
<entry>
<title>feat(setup-telega): install the telega Emacs package alongside docker setup</title>
<updated>2026-05-14T13:07:21+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-14T13:07:21+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1e21c2c31a44d3a2a9e302de4712b482de597058'/>
<id>urn:sha1:1e21c2c31a44d3a2a9e302de4712b482de597058</id>
<content type='text'>
modules/telega-config.el uses `:ensure nil' on the use-package block
(a stale MELPA archive index can 404 and take startup down if
auto-install runs in init).  The trade-off was that a fresh clone
needed a one-time `M-x package-install RET telega' before the
dashboard launcher or `C-; G' would work -- the autoload stub
would fail with `Cannot open load file: telega' instead.

Hit it on this machine just now: dashboard pressed, autoload tried
to load telega.el, no telega.el on the load-path, cryptic error.

Add `ensure_telega_package' to the setup script: probe with
`(package-installed-p 'telega)' under `emacs --batch'; if absent,
refresh MELPA and install via package.el; if that fails, surface
the manual recovery path.  Wire it into `main' after the docker
checks.  Four new bats tests cover the missing-emacs, already-
installed, install-succeeds, and install-fails paths with `emacs'
stubbed at the function level.
</content>
</entry>
<entry>
<title>feat(setup): scripts/setup-telega.sh prepares the docker environment for telega</title>
<updated>2026-05-13T21:11:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-13T21:11:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e179a1022319ec77d936f1cee21c471ac34f4d53'/>
<id>urn:sha1:e179a1022319ec77d936f1cee21c471ac34f4d53</id>
<content type='text'>
Verifies docker is installed and the daemon is reachable, then either pulls a public image (when `TELEGA_DOCKER_IMAGE` is set) or announces the in-Emacs build path (`M-x telega-server-build`) for the user to run once. Telegram auth (phone + verification code) is interactive on first `M-x telega` and not scripted here.

Same shape as setup-email.sh: helpers are sourceable for bats, `main` runs only under direct execution. 7 bats tests stub `docker` and `command` so the suite never talks to the real daemon.
</content>
</entry>
<entry>
<title>test: add terminal coverage summary helper</title>
<updated>2026-05-12T10:12:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-12T10:12:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=a032f45fe30ce300163bc052257c3c5c993c85d5'/>
<id>urn:sha1:a032f45fe30ce300163bc052257c3c5c993c85d5</id>
<content type='text'>
</content>
</entry>
<entry>
<title>test(scripts): add bats coverage for setup-email.sh password helpers</title>
<updated>2026-05-12T05:56:27+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-12T05:56:27+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=839bbeb14a92a777a3857102dba08a212b21443d'/>
<id>urn:sha1:839bbeb14a92a777a3857102dba08a212b21443d</id>
<content type='text'>
`setup-email.sh' ran top to bottom, so the only way to exercise `install_encrypted_password' / `decrypt_password' was to run the whole new-machine setup (mbsync, mu init). Its procedural body now lives in a `main()' function guarded by the usual `[[ "${BASH_SOURCE[0]}" == "${0}" ]]' check, so sourcing the script just defines the helpers, and running it directly is unchanged.

New `tests/test-setup-email.bats' sources the script, points the password dirs at a per-test tmpdir, and covers both helpers across the normal / skip-existing / missing-source / (for decrypt) gpg-failure paths, stubbing `gpg' so no real key is needed. `make test-bash' runs the bats files, and `make test' picks them up after the Elisp suite when bats is installed.
</content>
</entry>
<entry>
<title>feat(setup-email): add the deepsat work account</title>
<updated>2026-05-11T22:17:54+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-11T22:17:54+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=eddc103fd251e324f8bd5c9000ed8d29cb59c602'/>
<id>urn:sha1:eddc103fd251e324f8bd5c9000ed8d29cb59c602</id>
<content type='text'>
`setup-email.sh' was still gmail+cmail only. Added `dmail' as a first-class maildir (`~/.mail/dmail') and the work address to the `mu init' list, and reworked password bootstrap to match the live config: the gmail and dmail password files stay encrypted (mbsync/msmtp decrypt them on use), while cmail decrypts to `~/.config/.cmailpass' for ProtonBridge. A missing password source now fails loudly instead of continuing silently. `bash -n' verified. The script itself wasn't run, since it decrypts credentials, runs mbsync, and reindexes mu.
</content>
</entry>
<entry>
<title>feat(reveal): add org-reveal presentation workflow with ERT tests</title>
<updated>2026-02-14T09:21:13+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-02-14T09:21:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=ad4a5d7ba23a5871b241c4a41b3d1dede036a028'/>
<id>urn:sha1:ad4a5d7ba23a5871b241c4a41b3d1dede036a028</id>
<content type='text'>
Replaced pandoc-based reveal.js export with native ox-reveal integration.
New org-reveal-config.el module provides offline, self-contained HTML export
with keybindings under C-; p. Includes setup script for reveal.js 5.1.0
and 34 ERT tests covering header template and title-to-filename helpers.
</content>
</entry>
<entry>
<title>fix(keyboard): support daemon mode for M-S- key translations</title>
<updated>2026-02-09T22:11:42+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-02-09T22:11:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=1361215c177bb8b230d470badfc51d3499f414f5'/>
<id>urn:sha1:1361215c177bb8b230d470badfc51d3499f414f5</id>
<content type='text'>
GUI key translations (M-O → M-S-o, etc.) were never installed in daemon
mode because env-gui-p returns nil at startup with no frame. Use
server-after-make-frame-hook for daemon, emacs-startup-hook otherwise.
Also adds timestamps to assemblyai-transcribe output.
</content>
</entry>
<entry>
<title>feat(email): add password decryption to setup script</title>
<updated>2026-01-24T18:07:38+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-01-24T18:07:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=8645b13c41d16c839378a147ac055a790ffbc550'/>
<id>urn:sha1:8645b13c41d16c839378a147ac055a790ffbc550</id>
<content type='text'>
- Add password decryption loop to scripts/setup-email.sh
- Decrypt .gpg files from assets/mail-passwords/ to ~/.config/
- Add encrypted password files (.gmailpass.gpg, .cmailpass.gpg)
- Fix missing paren in text-config.el that broke config parsing
- Clean up mail-config.el
</content>
</entry>
</feed>
