feat: multi-calendar CalDAV sync + macOS install script
org-caldav: - Sync personal calendar (twoway) + family calendar (fromcal/read-only) - Clean helper fns: my/caldav-get-password, my/caldav-register-digest - authinfo: add 'family' entry for family calendar password - ~/.authinfo: machine cal.apps.sukany.cz login family password PASS macos-install.sh: - Bootstrap script for fresh macOS: brew, Emacs prereqs, search tools - Perl (cpanm + LSP/Tidy/Critic), Python (ruff/pyright), Go (gopls) - Ansible, Terraform, Podman, Kubernetes, email (mu/isync) - Doom Emacs setup guidance
This commit is contained in:
79
config.el
79
config.el
@@ -1744,38 +1744,59 @@ current frame."
|
|||||||
org-caldav-files '("~/org/personal.org" "~/org/work.org")
|
org-caldav-files '("~/org/personal.org" "~/org/work.org")
|
||||||
org-caldav-sync-direction 'twoway)
|
org-caldav-sync-direction 'twoway)
|
||||||
|
|
||||||
;; Baikal uses Digest auth. Pre-register credentials from ~/.authinfo
|
(defun my/caldav-get-password (host user)
|
||||||
;; so Emacs url package doesn't prompt interactively.
|
"Fetch plaintext password from auth-source for HOST and USER."
|
||||||
(defun my/caldav-setup-digest-auth ()
|
(let* ((found (car (auth-source-search :host host :user user :max 1)))
|
||||||
"Pre-register Baikal Digest auth. HA1 = MD5(user:realm:pass) from authinfo.
|
(s (when found (plist-get found :secret))))
|
||||||
Falls back to hardcoded hash if auth-source lookup fails.
|
(when s (if (functionp s) (funcall s) s))))
|
||||||
url-digest-auth-storage format: ((\"server:port\" (\"realm\" user ha1)) ...)"
|
|
||||||
(require 'url-auth)
|
(defun my/caldav-register-digest (user realm server plaintext-pass)
|
||||||
(let* ((user "martin")
|
"Register Digest auth for USER@SERVER with realm REALM.
|
||||||
(realm "BaikalDAV")
|
Stores HA1 = MD5(user:realm:pass) in url-digest-auth-storage."
|
||||||
(server "cal.apps.sukany.cz:443")
|
(when (and (boundp 'url-digest-auth-storage) plaintext-pass)
|
||||||
(found (car (auth-source-search :host "cal.apps.sukany.cz"
|
(let ((ha1 (md5 (concat user ":" realm ":" plaintext-pass))))
|
||||||
:user user :max 1)))
|
(setf (alist-get server url-digest-auth-storage nil nil #'equal)
|
||||||
(pass (when found
|
(list (list realm user ha1))))))
|
||||||
(let ((s (plist-get found :secret)))
|
|
||||||
(if (functionp s) (funcall s) s))))
|
|
||||||
;; Compute HA1 from authinfo password, or use pre-computed fallback
|
|
||||||
(ha1 (if pass
|
|
||||||
(md5 (concat user ":" realm ":" pass))
|
|
||||||
;; Fallback: MD5("martin:BaikalDAV:treasure-Hunter")
|
|
||||||
"7cf9c41c78f4986fd65948029bcc4743")))
|
|
||||||
(if (boundp 'url-digest-auth-storage)
|
|
||||||
;; Modern Emacs: inject into url-digest-auth-storage
|
|
||||||
(setq url-digest-auth-storage
|
|
||||||
(list (list server (list realm user ha1))))
|
|
||||||
;; Older Emacs: warn and skip
|
|
||||||
(message "org-caldav: url-digest-auth-storage not available, auth may fail"))))
|
|
||||||
|
|
||||||
(defun my/org-caldav-sync ()
|
(defun my/org-caldav-sync ()
|
||||||
"Sync org-caldav after pre-registering Baikal Digest auth."
|
"Sync org-caldav: personal calendar (twoway) + family calendar (read-only)."
|
||||||
(interactive)
|
(interactive)
|
||||||
(my/caldav-setup-digest-auth)
|
(require 'url-auth)
|
||||||
(org-caldav-sync)))
|
(let* ((host "cal.apps.sukany.cz")
|
||||||
|
(realm "BaikalDAV")
|
||||||
|
(srv "cal.apps.sukany.cz:443")
|
||||||
|
;; Get passwords from ~/.authinfo:
|
||||||
|
;; machine cal.apps.sukany.cz login martin password PASS
|
||||||
|
;; machine cal.apps.sukany.cz login family password PASS
|
||||||
|
(pass-m (my/caldav-get-password host "martin"))
|
||||||
|
(pass-f (my/caldav-get-password host "family")))
|
||||||
|
|
||||||
|
;; Fallback: pre-computed HA1 for martin if authinfo missing
|
||||||
|
(if pass-m
|
||||||
|
(my/caldav-register-digest "martin" realm srv pass-m)
|
||||||
|
(when (boundp 'url-digest-auth-storage)
|
||||||
|
(setf (alist-get srv url-digest-auth-storage nil nil #'equal)
|
||||||
|
(list (list realm "martin" "7cf9c41c78f4986fd65948029bcc4743")))))
|
||||||
|
|
||||||
|
;; Sync personal calendar (twoway — new events go here)
|
||||||
|
(setq org-caldav-url "https://cal.apps.sukany.cz/dav.php/calendars/martin"
|
||||||
|
org-caldav-calendar-id "default"
|
||||||
|
org-caldav-inbox "~/org/caldav-inbox.org"
|
||||||
|
org-caldav-files '("~/org/personal.org" "~/org/work.org")
|
||||||
|
org-caldav-sync-direction 'twoway)
|
||||||
|
(org-caldav-sync)
|
||||||
|
|
||||||
|
;; Sync family calendar (fromcal = read-only, no writes back)
|
||||||
|
(when pass-f
|
||||||
|
(my/caldav-register-digest "family" realm srv pass-f))
|
||||||
|
(setq org-caldav-url "https://cal.apps.sukany.cz/dav.php/calendars/family"
|
||||||
|
org-caldav-calendar-id "default"
|
||||||
|
org-caldav-inbox "~/org/family-calendar.org"
|
||||||
|
org-caldav-files nil
|
||||||
|
org-caldav-sync-direction 'fromcal)
|
||||||
|
(org-caldav-sync)
|
||||||
|
|
||||||
|
(message "CalDAV sync complete: personal (twoway) + family (read-only)"))))
|
||||||
|
|
||||||
(map! :leader "o c" #'my/org-caldav-sync)
|
(map! :leader "o c" #'my/org-caldav-sync)
|
||||||
|
|
||||||
|
|||||||
218
scripts/macos-install.sh
Executable file
218
scripts/macos-install.sh
Executable file
@@ -0,0 +1,218 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# macos-install.sh — Bootstrap Doom Emacs development environment on macOS
|
||||||
|
# Run once on a fresh machine. Idempotent — safe to re-run.
|
||||||
|
# Usage: bash scripts/macos-install.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${GREEN}▶${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
|
||||||
|
error() { echo -e "${RED}✗${NC} $*"; }
|
||||||
|
section() { echo -e "\n${GREEN}══ $* ══${NC}"; }
|
||||||
|
|
||||||
|
# ─── Homebrew ───────────────────────────────────────────────────────────────
|
||||||
|
section "Homebrew"
|
||||||
|
if ! command -v brew &>/dev/null; then
|
||||||
|
info "Installing Homebrew..."
|
||||||
|
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||||
|
else
|
||||||
|
info "Homebrew already installed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Core GNU tools (Doom prefers GNU over BSD) ──────────────────────────────
|
||||||
|
section "Core GNU tools"
|
||||||
|
brew install --quiet \
|
||||||
|
coreutils \
|
||||||
|
gnu-sed \
|
||||||
|
findutils \
|
||||||
|
grep \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
jq
|
||||||
|
|
||||||
|
# ─── Doom Emacs prerequisites ────────────────────────────────────────────────
|
||||||
|
section "Doom Emacs prerequisites"
|
||||||
|
brew install --quiet \
|
||||||
|
cmake \
|
||||||
|
libtool \
|
||||||
|
automake \
|
||||||
|
autoconf \
|
||||||
|
pkg-config \
|
||||||
|
texinfo \
|
||||||
|
sqlite \
|
||||||
|
tree-sitter
|
||||||
|
|
||||||
|
# ─── Emacs (if not already installed) ───────────────────────────────────────
|
||||||
|
section "Emacs"
|
||||||
|
if ! command -v emacs &>/dev/null; then
|
||||||
|
warn "Emacs not found. Install manually:"
|
||||||
|
warn " brew install --cask emacs # pre-built GUI"
|
||||||
|
warn " OR build from source with MacPorts/Homebrew for latest patches"
|
||||||
|
else
|
||||||
|
info "Emacs: $(emacs --version | head -1)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Search tools (Doom/Vertico/Consult use these) ───────────────────────────
|
||||||
|
section "Search & navigation tools"
|
||||||
|
brew install --quiet \
|
||||||
|
fd \
|
||||||
|
ripgrep \
|
||||||
|
fzf \
|
||||||
|
bat \
|
||||||
|
the_silver_searcher
|
||||||
|
|
||||||
|
# ─── Spell & grammar ─────────────────────────────────────────────────────────
|
||||||
|
section "Spell & grammar"
|
||||||
|
brew install --quiet \
|
||||||
|
ispell \
|
||||||
|
languagetool
|
||||||
|
|
||||||
|
# ─── Email (mu4e + mbsync) ───────────────────────────────────────────────────
|
||||||
|
section "Email — mu4e"
|
||||||
|
brew install --quiet \
|
||||||
|
mu \
|
||||||
|
isync
|
||||||
|
|
||||||
|
# ─── PDF tools ───────────────────────────────────────────────────────────────
|
||||||
|
section "PDF tools"
|
||||||
|
brew install --quiet \
|
||||||
|
poppler \
|
||||||
|
mupdf-tools
|
||||||
|
|
||||||
|
# ─── Fonts (Doom uses nerd-fonts) ────────────────────────────────────────────
|
||||||
|
section "Fonts"
|
||||||
|
brew install --quiet --cask \
|
||||||
|
font-jetbrains-mono-nerd-font \
|
||||||
|
font-fira-code-nerd-font \
|
||||||
|
2>/dev/null || warn "Font casks may need manual install from nerd-fonts"
|
||||||
|
|
||||||
|
# ─── Perl ────────────────────────────────────────────────────────────────────
|
||||||
|
section "Perl"
|
||||||
|
brew install --quiet perl
|
||||||
|
|
||||||
|
# Install cpanm
|
||||||
|
if ! command -v cpanm &>/dev/null; then
|
||||||
|
info "Installing cpanm..."
|
||||||
|
curl -L https://cpanmin.us | perl - App::cpanminus
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Installing Perl modules (LSP, Tidy, Critic)..."
|
||||||
|
cpanm --quiet --notest \
|
||||||
|
Perl::LanguageServer \
|
||||||
|
Perl::Tidy \
|
||||||
|
Perl::Critic \
|
||||||
|
App::perlimports \
|
||||||
|
2>/dev/null || warn "Some Perl modules failed — run cpanm manually if needed"
|
||||||
|
|
||||||
|
# ─── Python ──────────────────────────────────────────────────────────────────
|
||||||
|
section "Python"
|
||||||
|
brew install --quiet \
|
||||||
|
python \
|
||||||
|
pyenv \
|
||||||
|
ruff \
|
||||||
|
uv
|
||||||
|
|
||||||
|
# Pyright LSP
|
||||||
|
if ! command -v pyright &>/dev/null; then
|
||||||
|
info "Installing pyright..."
|
||||||
|
npm install -g pyright 2>/dev/null || warn "npm not found, install node first: brew install node"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Go ──────────────────────────────────────────────────────────────────────
|
||||||
|
section "Go"
|
||||||
|
brew install --quiet go
|
||||||
|
|
||||||
|
info "Installing Go tools..."
|
||||||
|
go install golang.org/x/tools/gopls@latest 2>/dev/null || warn "gopls install failed"
|
||||||
|
go install golang.org/x/tools/cmd/goimports@latest 2>/dev/null || warn "goimports install failed"
|
||||||
|
go install github.com/go-delve/delve/cmd/dlv@latest 2>/dev/null || warn "delve install failed"
|
||||||
|
|
||||||
|
# ─── Node.js (needed for some LSPs) ─────────────────────────────────────────
|
||||||
|
section "Node.js"
|
||||||
|
brew install --quiet node
|
||||||
|
|
||||||
|
# ─── Ansible ─────────────────────────────────────────────────────────────────
|
||||||
|
section "Ansible"
|
||||||
|
brew install --quiet \
|
||||||
|
ansible \
|
||||||
|
ansible-lint
|
||||||
|
|
||||||
|
# ─── Terraform ───────────────────────────────────────────────────────────────
|
||||||
|
section "Terraform"
|
||||||
|
brew install --quiet \
|
||||||
|
terraform \
|
||||||
|
terraform-ls \
|
||||||
|
tflint
|
||||||
|
|
||||||
|
# ─── Containers (Podman) ─────────────────────────────────────────────────────
|
||||||
|
section "Podman / Containers"
|
||||||
|
brew install --quiet \
|
||||||
|
podman \
|
||||||
|
hadolint
|
||||||
|
|
||||||
|
# ─── Kubernetes ──────────────────────────────────────────────────────────────
|
||||||
|
section "Kubernetes tools"
|
||||||
|
brew install --quiet \
|
||||||
|
kubectl \
|
||||||
|
kubectx
|
||||||
|
|
||||||
|
# ─── Version control extras ──────────────────────────────────────────────────
|
||||||
|
section "Git extras"
|
||||||
|
brew install --quiet \
|
||||||
|
git-delta \
|
||||||
|
gh
|
||||||
|
|
||||||
|
# ─── perltidy config (if missing) ────────────────────────────────────────────
|
||||||
|
section "perltidy config"
|
||||||
|
if [ ! -f "$HOME/.perltidyrc" ]; then
|
||||||
|
info "Creating ~/.perltidyrc..."
|
||||||
|
cat > "$HOME/.perltidyrc" << 'PERLTIDY'
|
||||||
|
-l=100
|
||||||
|
-i=4
|
||||||
|
-ci=4
|
||||||
|
-ce
|
||||||
|
-vt=0
|
||||||
|
-cti=0
|
||||||
|
-pt=0
|
||||||
|
-bt=0
|
||||||
|
-sbt=0
|
||||||
|
-bbt=0
|
||||||
|
-nsfs
|
||||||
|
-nolq
|
||||||
|
PERLTIDY
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Doom Emacs setup ────────────────────────────────────────────────────────
|
||||||
|
section "Doom Emacs"
|
||||||
|
if [ ! -d "$HOME/.emacs.d" ]; then
|
||||||
|
warn "Doom not installed. To install:"
|
||||||
|
warn " git clone --depth 1 https://github.com/doomemacs/doomemacs ~/.emacs.d"
|
||||||
|
warn " ~/.emacs.d/bin/doom install"
|
||||||
|
elif [ ! -d "$HOME/.doom.d" ]; then
|
||||||
|
warn "~/.doom.d missing. Clone your config:"
|
||||||
|
warn " git clone https://git.apps.sukany.cz/martin/emacs-doom ~/.doom.d"
|
||||||
|
warn " git clone https://git.apps.sukany.cz/martin/emacs-org ~/org"
|
||||||
|
warn " ~/.emacs.d/bin/doom sync"
|
||||||
|
else
|
||||||
|
info "Doom config found at ~/.doom.d"
|
||||||
|
info "Run: ~/.emacs.d/bin/doom sync"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Summary ─────────────────────────────────────────────────────────────────
|
||||||
|
section "Done"
|
||||||
|
echo ""
|
||||||
|
echo "Manual steps remaining:"
|
||||||
|
echo " 1. Create ~/.authinfo with CalDAV credentials"
|
||||||
|
echo " machine cal.apps.sukany.cz login martin password YOUR_PASS"
|
||||||
|
echo " 2. Configure mbsync: cp ~/.doom.d/examples/mbsyncrc ~/.mbsyncrc"
|
||||||
|
echo " 3. If Emacs built from source: sudo port install texinfo tree-sitter"
|
||||||
|
echo " 4. doom sync (in ~/.doom.d)"
|
||||||
|
echo " 5. doom doctor (check for remaining issues)"
|
||||||
|
echo ""
|
||||||
|
info "Installation complete."
|
||||||
Reference in New Issue
Block a user