From b4ce4be870191616cb638c5df21042ed4cc1fc7b Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 00:29:31 +0530 Subject: feat(office/calcurse): added configuration --- modules/office/calcurse/conf | 42 +++++++++++++++++++++++++++++++++++++ modules/office/calcurse/default.nix | 5 +++++ 2 files changed, 47 insertions(+) create mode 100644 modules/office/calcurse/conf (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/conf b/modules/office/calcurse/conf new file mode 100644 index 0000000..79e3189 --- /dev/null +++ b/modules/office/calcurse/conf @@ -0,0 +1,42 @@ +# Appearance +appearance.calendarview=monthly +appearance.compactpanels=no +appearance.defaultpanel=calendar +appearance.layout=1 +appearance.headerline=yes +appearance.eventseparator=yes +appearance.dayseparator=yes +appearance.emptyline=yes +appearance.emptyday=-- +appearance.notifybar=yes +appearance.sidebarwidth=0 +appearance.theme=green on black +appearance.todoview=hide-completed +appearance.headingpos=right-justified + +# Daemon +daemon.enable=no +daemon.log=no + +# Format +format.inputdate=1 +format.notifydate=%a %F +format.notifytime=%T +format.appointmenttime=%H:%M +format.outputdate=%D +format.dayheading=%B %e, %Y + +# General +general.autogc=no +general.autosave=yes +general.confirmdelete=yes +general.confirmquit=yes +general.firstdayofweek=monday +general.multipledays=yes +general.periodicsave=0 +general.systemevents=yes + +# Notification +notification.warning=300 +notification.command=printf '\a' +notification.notifyall=flagged-only diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index 03bf9c1..65390fd 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -18,5 +18,10 @@ config = lib.mkIf config.office.calcurse.enable { home.packages = [ config.office.calcurse.package ]; + + xdg.configFile."calcurse/conf" = { + source = ./conf; + force = true; + }; }; } -- cgit v1.2.3 From 2d3f222078fa5d569842a2e9fd316da1e566a7af Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 00:37:37 +0530 Subject: feat(office/calcurse): added keybindings --- modules/office/calcurse/default.nix | 5 ++++ modules/office/calcurse/keys | 51 +++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 modules/office/calcurse/keys (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index 65390fd..619ee23 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -23,5 +23,10 @@ source = ./conf; force = true; }; + + xdg.configFile."calcurse/keys" = { + source = ./keys; + force = true; + }; }; } diff --git a/modules/office/calcurse/keys b/modules/office/calcurse/keys new file mode 100644 index 0000000..1f45145 --- /dev/null +++ b/modules/office/calcurse/keys @@ -0,0 +1,51 @@ +# Calcurse keys configuration file +# actions and valid keys; bindings are `action key1 key2 ...` lines. + +generic-cancel ESC +generic-select SPC +generic-credits @ +generic-help ? +generic-quit q Q +generic-save s S ^S +generic-reload R +generic-copy c +generic-paste p ^V +generic-change-view TAB +generic-prev-view KEY_BTAB +generic-import i I +generic-export x X +generic-goto g G +generic-other-cmd o O +generic-config-menu C +generic-redraw ^R +generic-add-appt ^A +generic-add-todo ^T +generic-prev-day T ^H +generic-next-day t ^L +generic-prev-week W ^K +generic-next-week w +generic-prev-month M +generic-next-month m +generic-prev-year Y +generic-next-year y +generic-scroll-down ^N +generic-scroll-up ^P +generic-goto-today ^G +generic-command : +move-right l L RGT +move-left h H LFT +move-down j J DWN +move-up k K UP +start-of-week 0 +end-of-week $ +add-item a A +del-item d D +edit-item e E +view-item v V RET +pipe-item | +flag-item ! +repeat r +edit-note n N +view-note > +raise-priority + +lower-priority - -- cgit v1.2.3 From e61d4b9a561893ad1cc4220fa319b1d25287048a Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 01:13:38 +0530 Subject: feat(office/calcurse): wrapped calcurse with PAGER=nvim, todos show completed - using nvim as the pager for calcurse, seems to work better - now completed todos also appear in the todos section just at the bottom --- modules/office/calcurse/conf | 2 +- modules/office/calcurse/default.nix | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/conf b/modules/office/calcurse/conf index 79e3189..98aa9af 100644 --- a/modules/office/calcurse/conf +++ b/modules/office/calcurse/conf @@ -11,7 +11,7 @@ appearance.emptyday=-- appearance.notifybar=yes appearance.sidebarwidth=0 appearance.theme=green on black -appearance.todoview=hide-completed +appearance.todoview=show-completed appearance.headingpos=right-justified # Daemon diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index 619ee23..6de00cc 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -5,25 +5,39 @@ pkgs, ... }: +let + calcursePackage = + if config.development.nixvim.enable then + pkgs.symlinkJoin { + name = "calcurse-wrapped"; + paths = [ pkgs.calcurse ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/calcurse --set PAGER "nvim" + ''; + meta = pkgs.calcurse.meta // { + mainProgram = "calcurse"; + }; + } + else + pkgs.calcurse; +in { options.office.calcurse = { enable = lib.mkEnableOption "Enables calcurse."; package = lib.mkOption { type = lib.types.package; readOnly = true; - default = pkgs.calcurse; + default = calcursePackage; description = "The calcurse package to use."; }; }; - config = lib.mkIf config.office.calcurse.enable { home.packages = [ config.office.calcurse.package ]; - xdg.configFile."calcurse/conf" = { source = ./conf; force = true; }; - xdg.configFile."calcurse/keys" = { source = ./keys; force = true; -- cgit v1.2.3 From 59df727a1591ac4b47e202926669753731cabc69 Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 01:41:49 +0530 Subject: feat(office/calcurse): added calcurse-sync for git upstream syncing --- modules/office/calcurse/calcurse-sync.sh | 238 +++++++++++++++++++++++++++++++ modules/office/calcurse/default.nix | 43 ++++-- 2 files changed, 266 insertions(+), 15 deletions(-) create mode 100644 modules/office/calcurse/calcurse-sync.sh (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/calcurse-sync.sh b/modules/office/calcurse/calcurse-sync.sh new file mode 100644 index 0000000..0becb7a --- /dev/null +++ b/modules/office/calcurse/calcurse-sync.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash +# +# Keeps calcurse's data directory in git, so you can back it up / sync +# it across machines via a remote (e.g. a private GitHub repo). +# +# Usage: +# calcurse-sync init Turn the calcurse data dir into a git repo +# and (optionally) attach a remote. +# calcurse-sync sync Commit any changes and push. +# calcurse-sync pull Pull down changes from the remote. +# calcurse-sync status Show git status of the data directory. +# calcurse-sync remote Add/update the remote after the fact. +# calcurse-sync help Show this message. +# +# Defaults to $XDG_DATA_HOME/calcurse (usually ~/.local/share/calcurse). +# Override with $CALCURSE_DATA_DIR if yours lives elsewhere. + +set -euo pipefail + +DATA_DIR="${CALCURSE_DATA_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/calcurse}" +BRANCH="main" + +info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33m==> warning:\033[0m %s\n' "$*" >&2; } +error() { printf '\033[1;31m==> error:\033[0m %s\n' "$*" >&2; } +die() { error "$*"; exit 1; } + +require_git() { + command -v git >/dev/null 2>&1 \ + || die "git is not installed or not on PATH. Install it and try again." +} + +require_data_dir() { + if [ ! -d "$DATA_DIR" ]; then + die "calcurse data directory not found at: $DATA_DIR +Run calcurse at least once so it can create its data files, or set +CALCURSE_DATA_DIR to point at the right location." + fi +} + +require_repo() { + if [ ! -d "$DATA_DIR/.git" ]; then + die "no git repository found in $DATA_DIR +Run '$(basename "$0") init' first to set one up." + fi +} + +has_remote() { + git -C "$DATA_DIR" remote get-url origin >/dev/null 2>&1 +} + +has_upstream() { + git -C "$DATA_DIR" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1 +} + +# e.g. "25th May, 2026 at 12:34 PM" +readable_timestamp() { + local day suffix month_year time_part + + day=$(date +%-d) + case "$day" in + 1|21|31) suffix="st" ;; + 2|22) suffix="nd" ;; + 3|23) suffix="rd" ;; + *) suffix="th" ;; + esac + + month_year=$(date +"%B, %Y") + time_part=$(date +"%I:%M %p" | sed 's/^0//') + + printf '%s%s %s at %s' "$day" "$suffix" "$month_year" "$time_part" +} + +cmd_init() { + require_git + require_data_dir + + if [ -d "$DATA_DIR/.git" ]; then + die "a git repository already exists in $DATA_DIR +If you meant to reconfigure the remote, use '$(basename "$0") remote ' instead." + fi + + info "Initializing git repo in $DATA_DIR" + git -C "$DATA_DIR" init -b "$BRANCH" >/dev/null + + # calcurse sometimes drops lock/swap files in here; keep them out of git + if [ ! -e "$DATA_DIR/.gitignore" ]; then + cat > "$DATA_DIR/.gitignore" <<'EOF' +*.lock +*.swp +EOF + fi + + git -C "$DATA_DIR" add -A + if ! git -C "$DATA_DIR" diff --cached --quiet; then + git -C "$DATA_DIR" commit -m "sync: $(readable_timestamp)" >/dev/null + info "Created initial commit." + else + warn "Nothing to commit yet (data directory is empty)." + fi + + read -r -p "Remote URL to push to (leave blank to skip): " remote_url + if [ -n "$remote_url" ]; then + git -C "$DATA_DIR" remote add origin "$remote_url" + info "Pushing initial commit to $remote_url" + if git -C "$DATA_DIR" push -u origin "$BRANCH"; then + info "Repo initialized and pushed." + else + error "Push failed. The remote was still added — check the URL and your" + error "credentials, then try '$(basename "$0") sync' again." + exit 1 + fi + else + warn "No remote configured. This repo will only track history locally" + warn "until you run '$(basename "$0") remote '." + fi +} + +cmd_remote() { + require_git + require_repo + + local url="${1:-}" + [ -n "$url" ] || die "usage: $(basename "$0") remote " + + if has_remote; then + git -C "$DATA_DIR" remote set-url origin "$url" + info "Updated remote 'origin' to $url" + else + git -C "$DATA_DIR" remote add origin "$url" + info "Added remote 'origin' pointing to $url" + fi +} + +cmd_sync() { + require_git + require_data_dir + require_repo + + cd "$DATA_DIR" + git add -A + + if ! git diff --cached --quiet; then + git commit -m "sync: $(readable_timestamp)" >/dev/null + info "Committed changes." + else + info "No local changes to commit." + fi + + if ! has_remote; then + warn "No remote configured for this repo — commit saved locally only." + warn "Run '$(basename "$0") remote ' to enable pushing." + return 0 + fi + + if has_upstream && [ -z "$(git log '@{u}..HEAD' --oneline)" ]; then + info "Already up to date with the remote." + return 0 + fi + + info "Pushing to remote..." + if has_upstream; then + push_cmd=(git push) + else + push_cmd=(git push -u origin "$BRANCH") + fi + + if "${push_cmd[@]}"; then + info "Sync complete." + else + status=$? + error "Push failed." + error "This usually means the remote has commits you don't have locally" + error "(e.g. synced from another machine). Run '$(basename "$0") pull'" + error "to merge them in, then sync again. If that's not it, check your" + error "network connection and git credentials." + exit "$status" + fi +} + +cmd_pull() { + require_git + require_data_dir + require_repo + + if ! has_remote; then + die "no remote configured for this repo. +Run '$(basename "$0") remote ' first." + fi + + cd "$DATA_DIR" + + if [ -n "$(git status --porcelain)" ]; then + warn "You have uncommitted changes in $DATA_DIR." + warn "Run '$(basename "$0") sync' first so a pull can't clobber them." + die "aborting pull to avoid data loss." + fi + + info "Pulling from remote..." + if git pull --rebase origin "$BRANCH"; then + info "Up to date." + else + error "Pull failed — likely a merge conflict." + error "Resolve conflicts manually in $DATA_DIR, then run:" + error " git -C \"$DATA_DIR\" rebase --continue" + error "(or 'git rebase --abort' to back out)." + exit 1 + fi +} + +cmd_status() { + require_git + require_data_dir + require_repo + + git -C "$DATA_DIR" status +} + +cmd_help() { + sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//' +} + +main() { + local sub="${1:-help}" + shift || true + + case "$sub" in + init) cmd_init "$@" ;; + sync) cmd_sync "$@" ;; + pull) cmd_pull "$@" ;; + status) cmd_status "$@" ;; + remote) cmd_remote "$@" ;; + help|-h|--help) cmd_help ;; + *) die "unknown command: $sub (try '$(basename "$0") help')" ;; + esac +} + +main "$@" diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index 6de00cc..15fc452 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -6,21 +6,34 @@ ... }: let - calcursePackage = - if config.development.nixvim.enable then - pkgs.symlinkJoin { - name = "calcurse-wrapped"; - paths = [ pkgs.calcurse ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/calcurse --set PAGER "nvim" - ''; - meta = pkgs.calcurse.meta // { - mainProgram = "calcurse"; - }; - } - else - pkgs.calcurse; + calcurseSync = pkgs.writeShellScriptBin "calcurse-sync" (builtins.readFile ./calcurse-sync.sh); + + calcursePackage = pkgs.symlinkJoin { + name = "calcurse-wrapped"; + paths = [ + pkgs.bash + pkgs.calcurse + calcurseSync + ] + ++ lib.optionals config.development.nixvim.enable [ config.development.nixvim.package ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + ${lib.optionalString config.development.nixvim.enable '' + wrapProgram $out/bin/calcurse --set PAGER "nvim" + ''} + wrapProgram $out/bin/calcurse-sync \ + --prefix PATH : ${ + lib.makeBinPath [ + config.development.git.package + pkgs.coreutils + pkgs.gnused + ] + } + ''; + meta = { + mainProgram = "calcurse"; + }; + }; in { options.office.calcurse = { -- cgit v1.2.3 From 0afbd64a18aef6c048fba40d1c1350dbcdc4930a Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 02:34:32 +0530 Subject: feat(office/calcurse): added hooks for calcurse-sync - saving and loading automatically syncs the local with a remote upstream git repo --- modules/office/calcurse/default.nix | 10 ++++++++++ modules/office/calcurse/hooks/post-save | 16 ++++++++++++++++ modules/office/calcurse/hooks/pre-load | 16 ++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 modules/office/calcurse/hooks/post-save create mode 100644 modules/office/calcurse/hooks/pre-load (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index 15fc452..2825b41 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -55,5 +55,15 @@ in source = ./keys; force = true; }; + xdg.configFile."calcurse/hooks/pre-load" = { + source = ./hooks/pre-load; + executable = true; + force = true; + }; + xdg.configFile."calcurse/hooks/post-save" = { + source = ./hooks/post-save; + executable = true; + force = true; + }; }; } diff --git a/modules/office/calcurse/hooks/post-save b/modules/office/calcurse/hooks/post-save new file mode 100644 index 0000000..5d2ab1d --- /dev/null +++ b/modules/office/calcurse/hooks/post-save @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Commits and pushes the calcurse data dir after every save. +# +# Hooks hand calcurse's terminal over to whatever they run and calcurse +# waits for them to exit before redrawing, so this backgrounds the actual +# sync instead of blocking the UI on a git push. Output goes to a log +# file rather than stdout/stderr, since hooks aren't meant to be +# interactive. + +log="${XDG_DATA_HOME:-$HOME/.local/share}/calcurse/hooks.log" + +( + printf '[%s] post-save\n' "$(date '+%Y-%m-%d %H:%M:%S')" + calcurse-sync sync +) >>"$log" 2>&1 & +disown diff --git a/modules/office/calcurse/hooks/pre-load b/modules/office/calcurse/hooks/pre-load new file mode 100644 index 0000000..bd87af3 --- /dev/null +++ b/modules/office/calcurse/hooks/pre-load @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Pulls the latest calcurse data before calcurse reads it, so a fresh +# session (or a ^R reload) starts from wherever you last left off on +# another machine. +# +# Runs synchronously, unlike post-save, since the point is to have +# up-to-date data before it's loaded. Never fails calcurse's startup, +# though: if the pull can't complete (offline, no remote configured +# yet, a conflict), calcurse just opens with whatever's on disk. + +log="${XDG_DATA_HOME:-$HOME/.local/share}/calcurse/hooks.log" + +{ + printf '[%s] pre-load\n' "$(date '+%Y-%m-%d %H:%M:%S')" + calcurse-sync pull +} >>"$log" 2>&1 || true -- cgit v1.2.3 From 3a0976e4a6a504d204ebff54ec67bce8a04025f9 Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 16:01:07 +0530 Subject: feat(office/calcurse): added git configuration for calcurse syncing repo --- modules/office/calcurse/default.nix | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index 2825b41..e8b38b6 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -65,5 +65,23 @@ in executable = true; force = true; }; + + programs.git.includes = lib.mkIf config.development.git.enable [ + { + condition = "gitdir:${config.xdg.dataHome}/calcurse/"; + contents = { + user = { + name = "Calcurse of ${config.home.username}"; + email = "${config.home.username}@calcurse.localhost"; + }; + commit = { + gpgSign = false; + }; + tag = { + gpgSign = false; + }; + }; + } + ]; }; } -- cgit v1.2.3 From f95d0db80adb610be18cc766c50d19d98d0a14ef Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 16:18:40 +0530 Subject: chore(office/calcurse): fixed calcurse-sync logs - calcurse-sync now refers to itself as "calcurse-sync" instead of using $(basename "$0") to find its own filename --- modules/office/calcurse/calcurse-sync.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/calcurse-sync.sh b/modules/office/calcurse/calcurse-sync.sh index 0becb7a..ac366e8 100644 --- a/modules/office/calcurse/calcurse-sync.sh +++ b/modules/office/calcurse/calcurse-sync.sh @@ -41,7 +41,7 @@ CALCURSE_DATA_DIR to point at the right location." require_repo() { if [ ! -d "$DATA_DIR/.git" ]; then die "no git repository found in $DATA_DIR -Run '$(basename "$0") init' first to set one up." +Run 'calcurse-sync init' first to set one up." fi } @@ -77,7 +77,7 @@ cmd_init() { if [ -d "$DATA_DIR/.git" ]; then die "a git repository already exists in $DATA_DIR -If you meant to reconfigure the remote, use '$(basename "$0") remote ' instead." +If you meant to reconfigure the remote, use 'calcurse-sync remote ' instead." fi info "Initializing git repo in $DATA_DIR" @@ -107,12 +107,12 @@ EOF info "Repo initialized and pushed." else error "Push failed. The remote was still added — check the URL and your" - error "credentials, then try '$(basename "$0") sync' again." + error "credentials, then try 'calcurse-sync sync' again." exit 1 fi else warn "No remote configured. This repo will only track history locally" - warn "until you run '$(basename "$0") remote '." + warn "until you run 'calcurse-sync remote '." fi } @@ -121,7 +121,7 @@ cmd_remote() { require_repo local url="${1:-}" - [ -n "$url" ] || die "usage: $(basename "$0") remote " + [ -n "$url" ] || die "usage: calcurse-sync remote " if has_remote; then git -C "$DATA_DIR" remote set-url origin "$url" @@ -149,7 +149,7 @@ cmd_sync() { if ! has_remote; then warn "No remote configured for this repo — commit saved locally only." - warn "Run '$(basename "$0") remote ' to enable pushing." + warn "Run 'calcurse-sync remote ' to enable pushing." return 0 fi @@ -171,7 +171,7 @@ cmd_sync() { status=$? error "Push failed." error "This usually means the remote has commits you don't have locally" - error "(e.g. synced from another machine). Run '$(basename "$0") pull'" + error "(e.g. synced from another machine). Run 'calcurse-sync pull'" error "to merge them in, then sync again. If that's not it, check your" error "network connection and git credentials." exit "$status" @@ -185,14 +185,14 @@ cmd_pull() { if ! has_remote; then die "no remote configured for this repo. -Run '$(basename "$0") remote ' first." +Run 'calcurse-sync remote ' first." fi cd "$DATA_DIR" if [ -n "$(git status --porcelain)" ]; then warn "You have uncommitted changes in $DATA_DIR." - warn "Run '$(basename "$0") sync' first so a pull can't clobber them." + warn "Run 'calcurse-sync sync' first so a pull can't clobber them." die "aborting pull to avoid data loss." fi @@ -231,7 +231,7 @@ main() { status) cmd_status "$@" ;; remote) cmd_remote "$@" ;; help|-h|--help) cmd_help ;; - *) die "unknown command: $sub (try '$(basename "$0") help')" ;; + *) die "unknown command: $sub (try 'calcurse-sync help')" ;; esac } -- cgit v1.2.3 From 2fdefe79b1ddc31241c22a7f88ae931ddc4180f3 Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 16:34:20 +0530 Subject: feat(office/calcurse): calcurse-sync sync autoruns init --- modules/office/calcurse/calcurse-sync.sh | 56 +++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/calcurse-sync.sh b/modules/office/calcurse/calcurse-sync.sh index ac366e8..8251e06 100644 --- a/modules/office/calcurse/calcurse-sync.sh +++ b/modules/office/calcurse/calcurse-sync.sh @@ -6,7 +6,8 @@ # Usage: # calcurse-sync init Turn the calcurse data dir into a git repo # and (optionally) attach a remote. -# calcurse-sync sync Commit any changes and push. +# calcurse-sync sync Commit any changes and push. Initializes +# the repo automatically on first run. # calcurse-sync pull Pull down changes from the remote. # calcurse-sync status Show git status of the data directory. # calcurse-sync remote Add/update the remote after the fact. @@ -45,6 +46,10 @@ Run 'calcurse-sync init' first to set one up." fi } +repo_exists() { + [ -d "$DATA_DIR/.git" ] +} + has_remote() { git -C "$DATA_DIR" remote get-url origin >/dev/null 2>&1 } @@ -71,16 +76,11 @@ readable_timestamp() { printf '%s%s %s at %s' "$day" "$suffix" "$month_year" "$time_part" } -cmd_init() { - require_git - require_data_dir - - if [ -d "$DATA_DIR/.git" ]; then - die "a git repository already exists in $DATA_DIR -If you meant to reconfigure the remote, use 'calcurse-sync remote ' instead." - fi - - info "Initializing git repo in $DATA_DIR" +# Initializes the git repo, makes an initial commit if there's anything to +# commit, and prompts for a remote to push to. Shared by `init` (explicit, +# manual) and `sync` (automatic, first-run-on-a-new-device). +do_init() { + info "No git repository found in $DATA_DIR — initializing one." git -C "$DATA_DIR" init -b "$BRANCH" >/dev/null # calcurse sometimes drops lock/swap files in here; keep them out of git @@ -88,18 +88,20 @@ If you meant to reconfigure the remote, use 'calcurse-sync remote ' instead cat > "$DATA_DIR/.gitignore" <<'EOF' *.lock *.swp +*.pid +*.log EOF fi - git -C "$DATA_DIR" add -A - if ! git -C "$DATA_DIR" diff --cached --quiet; then - git -C "$DATA_DIR" commit -m "sync: $(readable_timestamp)" >/dev/null - info "Created initial commit." + local remote_url="" + if [ -t 0 ]; then + read -r -p "Remote URL to push to (leave blank to skip): " remote_url else - warn "Nothing to commit yet (data directory is empty)." + warn "Running non-interactively (e.g. from a calcurse hook) — skipping" + warn "the remote-URL prompt. Run 'calcurse-sync remote ' once you're" + warn "at a terminal to enable pushing." fi - read -r -p "Remote URL to push to (leave blank to skip): " remote_url if [ -n "$remote_url" ]; then git -C "$DATA_DIR" remote add origin "$remote_url" info "Pushing initial commit to $remote_url" @@ -110,12 +112,24 @@ EOF error "credentials, then try 'calcurse-sync sync' again." exit 1 fi - else + elif [ -t 0 ]; then warn "No remote configured. This repo will only track history locally" warn "until you run 'calcurse-sync remote '." fi } +cmd_init() { + require_git + require_data_dir + + if repo_exists; then + die "a git repository already exists in $DATA_DIR +If you meant to reconfigure the remote, use 'calcurse-sync remote ' instead." + fi + + do_init +} + cmd_remote() { require_git require_repo @@ -135,7 +149,11 @@ cmd_remote() { cmd_sync() { require_git require_data_dir - require_repo + + if ! repo_exists; then + do_init + return 0 + fi cd "$DATA_DIR" git add -A -- cgit v1.2.3 From d5506ca50709f0b786d8ca8329f7cdbb7df9142e Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 16:49:38 +0530 Subject: feat(office/calcurse): calcurse-sync can now be setup in home-manager --- modules/office/calcurse/calcurse-sync.sh | 46 ++++++++----- modules/office/calcurse/default.nix | 110 ++++++++++++++++++------------- 2 files changed, 93 insertions(+), 63 deletions(-) (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/calcurse-sync.sh b/modules/office/calcurse/calcurse-sync.sh index 8251e06..169adc9 100644 --- a/modules/office/calcurse/calcurse-sync.sh +++ b/modules/office/calcurse/calcurse-sync.sh @@ -4,8 +4,12 @@ # it across machines via a remote (e.g. a private GitHub repo). # # Usage: -# calcurse-sync init Turn the calcurse data dir into a git repo -# and (optionally) attach a remote. +# calcurse-sync init [remote-url] Turn the calcurse data dir into a git +# repo and (optionally) attach a remote. +# Safe to re-run — no-ops if a repo +# already exists. If no remote is given, +# falls back to $CALCURSE_SYNC_REMOTE, +# then (if interactive) a prompt. # calcurse-sync sync Commit any changes and push. Initializes # the repo automatically on first run. # calcurse-sync pull Pull down changes from the remote. @@ -15,6 +19,7 @@ # # Defaults to $XDG_DATA_HOME/calcurse (usually ~/.local/share/calcurse). # Override with $CALCURSE_DATA_DIR if yours lives elsewhere. +# Override the default remote used on first init with $CALCURSE_SYNC_REMOTE. set -euo pipefail @@ -77,9 +82,13 @@ readable_timestamp() { } # Initializes the git repo, makes an initial commit if there's anything to -# commit, and prompts for a remote to push to. Shared by `init` (explicit, -# manual) and `sync` (automatic, first-run-on-a-new-device). +# commit, and attaches a remote. The remote is resolved in this order: +# explicit arg > $CALCURSE_SYNC_REMOTE > interactive prompt (tty only) > +# skipped. Shared by `init` (explicit/manual) and `sync`/activation +# (automatic, first-run-on-a-new-device). do_init() { + local remote_url="${1:-${CALCURSE_SYNC_REMOTE:-}}" + info "No git repository found in $DATA_DIR — initializing one." git -C "$DATA_DIR" init -b "$BRANCH" >/dev/null @@ -88,18 +97,19 @@ do_init() { cat > "$DATA_DIR/.gitignore" <<'EOF' *.lock *.swp -*.pid -*.log EOF fi - local remote_url="" - if [ -t 0 ]; then - read -r -p "Remote URL to push to (leave blank to skip): " remote_url + git -C "$DATA_DIR" add -A + if ! git -C "$DATA_DIR" diff --cached --quiet; then + git -C "$DATA_DIR" commit -m "sync: $(readable_timestamp)" >/dev/null + info "Created initial commit." else - warn "Running non-interactively (e.g. from a calcurse hook) — skipping" - warn "the remote-URL prompt. Run 'calcurse-sync remote ' once you're" - warn "at a terminal to enable pushing." + warn "Nothing to commit yet (data directory is empty)." + fi + + if [ -z "$remote_url" ] && [ -t 0 ]; then + read -r -p "Remote URL to push to (leave blank to skip): " remote_url fi if [ -n "$remote_url" ]; then @@ -112,9 +122,9 @@ EOF error "credentials, then try 'calcurse-sync sync' again." exit 1 fi - elif [ -t 0 ]; then + else warn "No remote configured. This repo will only track history locally" - warn "until you run 'calcurse-sync remote '." + warn "until you run 'calcurse-sync remote ' (or set CALCURSE_SYNC_REMOTE)." fi } @@ -123,11 +133,11 @@ cmd_init() { require_data_dir if repo_exists; then - die "a git repository already exists in $DATA_DIR -If you meant to reconfigure the remote, use 'calcurse-sync remote ' instead." + info "Repository already exists in $DATA_DIR — nothing to do." + return 0 fi - do_init + do_init "${1:-}" } cmd_remote() { @@ -235,7 +245,7 @@ cmd_status() { } cmd_help() { - sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//' + sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//' } main() { diff --git a/modules/office/calcurse/default.nix b/modules/office/calcurse/default.nix index e8b38b6..c72896e 100644 --- a/modules/office/calcurse/default.nix +++ b/modules/office/calcurse/default.nix @@ -13,22 +13,27 @@ let paths = [ pkgs.bash pkgs.calcurse - calcurseSync ] - ++ lib.optionals config.development.nixvim.enable [ config.development.nixvim.package ]; + ++ lib.optionals config.development.nixvim.enable [ config.development.nixvim.package ] + ++ lib.optionals config.office.calcurse.sync.enable [ calcurseSync ]; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' ${lib.optionalString config.development.nixvim.enable '' wrapProgram $out/bin/calcurse --set PAGER "nvim" ''} - wrapProgram $out/bin/calcurse-sync \ - --prefix PATH : ${ - lib.makeBinPath [ - config.development.git.package - pkgs.coreutils - pkgs.gnused - ] - } + ${lib.optionalString config.office.calcurse.sync.enable '' + wrapProgram $out/bin/calcurse-sync \ + --prefix PATH : ${ + lib.makeBinPath [ + config.development.git.package + pkgs.coreutils + pkgs.gnused + ] + } \ + ${lib.optionalString (config.office.calcurse.sync.remote != null) '' + --set CALCURSE_SYNC_REMOTE ${lib.escapeShellArg config.office.calcurse.sync.remote} + ''} + ''} ''; meta = { mainProgram = "calcurse"; @@ -44,44 +49,59 @@ in default = calcursePackage; description = "The calcurse package to use."; }; - }; - config = lib.mkIf config.office.calcurse.enable { - home.packages = [ config.office.calcurse.package ]; - xdg.configFile."calcurse/conf" = { - source = ./conf; - force = true; - }; - xdg.configFile."calcurse/keys" = { - source = ./keys; - force = true; - }; - xdg.configFile."calcurse/hooks/pre-load" = { - source = ./hooks/pre-load; - executable = true; - force = true; - }; - xdg.configFile."calcurse/hooks/post-save" = { - source = ./hooks/post-save; - executable = true; - force = true; + sync = { + enable = lib.mkEnableOption "Enables git-backed syncing of the calcurse data directory (builds calcurse-sync, installs the sync hooks, and runs an initial 'calcurse-sync init' on activation)."; + remote = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = "Git remote URL for the calcurse data directory. When set, calcurse-sync uses it automatically on first init instead of prompting."; + }; }; + }; - programs.git.includes = lib.mkIf config.development.git.enable [ + config = lib.mkIf config.office.calcurse.enable ( + lib.mkMerge [ { - condition = "gitdir:${config.xdg.dataHome}/calcurse/"; - contents = { - user = { - name = "Calcurse of ${config.home.username}"; - email = "${config.home.username}@calcurse.localhost"; - }; - commit = { - gpgSign = false; - }; - tag = { - gpgSign = false; - }; + home.packages = [ config.office.calcurse.package ]; + xdg.configFile."calcurse/conf" = { + source = ./conf; + force = true; + }; + xdg.configFile."calcurse/keys" = { + source = ./keys; + force = true; }; } - ]; - }; + (lib.mkIf config.office.calcurse.sync.enable { + xdg.configFile."calcurse/hooks/pre-load" = { + source = ./hooks/pre-load; + executable = true; + force = true; + }; + xdg.configFile."calcurse/hooks/post-save" = { + source = ./hooks/post-save; + executable = true; + force = true; + }; + + programs.git.includes = lib.mkIf config.development.git.enable [ + { + condition = "gitdir:${config.xdg.dataHome}/calcurse/"; + contents = { + user = { + name = "Calcurse of ${config.home.username}"; + email = "${config.home.username}@calcurse.localhost"; + }; + commit.gpgSign = false; + tag.gpgSign = false; + }; + } + ]; + + home.activation.calcurseSyncInit = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + run ${config.office.calcurse.package}/bin/calcurse-sync init || true + ''; + }) + ] + ); } -- cgit v1.2.3 From 894f96eb131427041c2bb726b7b798edf4f683dc Mon Sep 17 00:00:00 2001 From: Arpit Chakladar Date: Wed, 2 Sep 2026 17:01:45 +0530 Subject: feat(office/calcurse): added timeout for remote syncing - git pull and git pull are now set to timeout at 8s, this is to ensure that it works even in offline environments --- modules/office/calcurse/calcurse-sync.sh | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'modules/office/calcurse') diff --git a/modules/office/calcurse/calcurse-sync.sh b/modules/office/calcurse/calcurse-sync.sh index 169adc9..8323bff 100644 --- a/modules/office/calcurse/calcurse-sync.sh +++ b/modules/office/calcurse/calcurse-sync.sh @@ -25,6 +25,7 @@ set -euo pipefail DATA_DIR="${CALCURSE_DATA_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/calcurse}" BRANCH="main" +SYNC_TIMEOUT="8" # seconds before a network op gives up info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m==> warning:\033[0m %s\n' "$*" >&2; } @@ -188,15 +189,20 @@ cmd_sync() { info "Pushing to remote..." if has_upstream; then - push_cmd=(git push) + push_cmd=(timeout "$SYNC_TIMEOUT" git push) else - push_cmd=(git push -u origin "$BRANCH") + push_cmd=(timeout "$SYNC_TIMEOUT" git push -u origin "$BRANCH") fi if "${push_cmd[@]}"; then info "Sync complete." else status=$? + if [ "$status" -eq 124 ]; then + warn "Push timed out after ${SYNC_TIMEOUT}s — commit saved locally, will" + warn "retry on next sync." + return 0 + fi error "Push failed." error "This usually means the remote has commits you don't have locally" error "(e.g. synced from another machine). Run 'calcurse-sync pull'" @@ -212,8 +218,8 @@ cmd_pull() { require_repo if ! has_remote; then - die "no remote configured for this repo. -Run 'calcurse-sync remote ' first." + warn "no remote configured for this repo — skipping pull." + return 0 fi cd "$DATA_DIR" @@ -224,10 +230,17 @@ Run 'calcurse-sync remote ' first." die "aborting pull to avoid data loss." fi - info "Pulling from remote..." - if git pull --rebase origin "$BRANCH"; then + info "Pulling from remote (timeout ${SYNC_TIMEOUT}s)..." + if timeout "$SYNC_TIMEOUT" git pull --rebase origin "$BRANCH"; then info "Up to date." else + status=$? + if [ "$status" -eq 124 ]; then + warn "Pull timed out after ${SYNC_TIMEOUT}s — continuing with local data." + warn "Set \$CALCURSE_SYNC_TIMEOUT to a higher value if your connection" + warn "is just slow, not down." + return 0 + fi error "Pull failed — likely a merge conflict." error "Resolve conflicts manually in $DATA_DIR, then run:" error " git -C \"$DATA_DIR\" rebase --continue" -- cgit v1.2.3