aboutsummaryrefslogtreecommitdiffstats
path: root/modules/office/calcurse
diff options
context:
space:
mode:
authorArpit Chakladar <arpitchakladar+git@gmail.com>2026-09-02 01:41:49 +0530
committerArpit Chakladar <arpitchakladar+git@gmail.com>2026-09-02 01:41:49 +0530
commit59df727a1591ac4b47e202926669753731cabc69 (patch)
tree4ac51643280f4f95ba29af2e5b89c76dd6deb310 /modules/office/calcurse
parente61d4b9a561893ad1cc4220fa319b1d25287048a (diff)
downloadhome-manager-config-59df727a1591ac4b47e202926669753731cabc69.tar.gz
home-manager-config-59df727a1591ac4b47e202926669753731cabc69.zip
feat(office/calcurse): added calcurse-sync for git upstream syncing
Diffstat (limited to 'modules/office/calcurse')
-rw-r--r--modules/office/calcurse/calcurse-sync.sh238
-rw-r--r--modules/office/calcurse/default.nix43
2 files changed, 266 insertions, 15 deletions
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 <url> 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 <url>' 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 <url>'."
+ fi
+}
+
+cmd_remote() {
+ require_git
+ require_repo
+
+ local url="${1:-}"
+ [ -n "$url" ] || die "usage: $(basename "$0") remote <url>"
+
+ 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 <url>' 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 <url>' 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 = {