Initial commit
authorChristian Weiske <[email protected]>
Tue, 12 Dec 2023 20:21:07 +0000 (21:21 +0100)
committerChristian Weiske <[email protected]>
Wed, 13 Dec 2023 20:01:47 +0000 (21:01 +0100)
.gitignore [new file with mode: 0644]
.gitignore-contacts [new file with mode: 0644]
README.rst [new file with mode: 0644]
backup.sh [new file with mode: 0755]
create-commit-message.sh [new file with mode: 0755]
vdirsyncer-config.example [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a944a35
--- /dev/null
@@ -0,0 +1,2 @@
+contacts
+README.html
diff --git a/.gitignore-contacts b/.gitignore-contacts
new file mode 100644 (file)
index 0000000..11cc80f
--- /dev/null
@@ -0,0 +1,2 @@
+.password-*
+vdirsyncer-status
diff --git a/README.rst b/README.rst
new file mode 100644 (file)
index 0000000..8c7c536
--- /dev/null
@@ -0,0 +1,39 @@
+Backup CardDAV vCards into git
+==============================
+
+Small script to backup address books stored a CardDAV server into
+a local git repository for backup purposes.
+Git commit messages contain the names of modified, added and deleted contacts
+so it's easy to find out when someone was modified, and thus easy to find
+lost contacts.
+
+Tested with vdirsyncer 0.19.2 and Nextcloud 23.
+
+
+Setup
+-----
+1. Install `vdirsyncer`__ 0.19.x
+2. Clone this repository
+3. Run ``./backup.sh``. Directory ``contacts`` will be created and a
+   sample sync configuration will be added
+4. In ``contacts`` directory adjust ``vdirsyncer-config``
+   and test it with::
+
+     vdirsyncer --config=vdirsyncer-config discover
+
+   Create all local collections when asked.
+5. In ``contacts`` run::
+
+     vdirsyncer --config=vdirsyncer-config metasync
+
+6. Run ``./backup.sh`` which should commit all contacts to git.
+7. Setup a cronjob on your home server to run ``backup.sh`` every night
+
+
+__ http://vdirsyncer.pimutils.org/en/stable/
+
+
+About
+-----
+This script was written by `Christian Weiske <https://cweiske.de/>`_
+and is licensed under the AGPL v3.
diff --git a/backup.sh b/backup.sh
new file mode 100755 (executable)
index 0000000..763250d
--- /dev/null
+++ b/backup.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# download vcard contacts into contacts/ and commit them to git
+set -e
+
+export GIT_AUTHOR_NAME="vcard backup"
+export GIT_AUTHOR_EMAIL="[email protected]"
+export GIT_COMMITTER_NAME="vcard backup"
+export GIT_COMMITTER_EMAIL="[email protected]"
+
+[ -d contacts ] || mkdir contacts
+
+cd contacts
+[ -d .git ] || git init
+[ -f .gitignore ] || cp ../.gitignore-contacts .gitignore
+
+if [ ! -f vdirsyncer-config ]; then
+    cp ../vdirsyncer-config.example vdirsyncer-config
+    echo "Adjust contacts/vdirsyncer-config first"
+    exit 1
+fi
+
+# commit configuration files if necessary
+conffiles=".gitignore vdirsyncer-config"
+metamod=$(git status --porcelain $conffiles)
+if [ -n "$metamod" ]; then
+    git add $conffiles
+    git commit $conffiles -m 'Update configuration'
+fi
+
+# sync and commit vcards
+vdirsyncer --config=vdirsyncer-config --verbosity WARNING sync
+numchanged=$(git status --porcelain | wc -l)
+if [ $numchanged -eq 0 ]; then
+    exit 0
+fi
+
+git add .
+../create-commit-message.sh > tmp-msg
+git commit --quiet --file=tmp-msg
+rm tmp-msg
diff --git a/create-commit-message.sh b/create-commit-message.sh
new file mode 100755 (executable)
index 0000000..070f79a
--- /dev/null
@@ -0,0 +1,63 @@
+#!/bin/sh
+#create a nice commit message that contains the names
+# of all modified, added and deleted contacts
+# to make it easier to find changes in the git history
+set -e
+
+numadd=$(git status --porcelain -uno|grep ^A|wc -l)
+numdel=$(git status --porcelain -uno|grep ^D|wc -l)
+nummod=$(git status --porcelain -uno|grep ^M|wc -l)
+if [ "$numadd" -eq 0 -a "$numdel" -eq 0 -a "$nummod" -eq 0 ]; then
+    exit 0
+fi
+
+echo "Backup $(date +%F): ~$nummod +$numadd -$numdel"
+echo
+
+for addressbook in $(ls -1d */* | grep -v vdirsyncer-status/); do
+    user=$(echo $addressbook | cut -d/ -f1)
+    addressbookTitle=$(cat $addressbook/displayname)
+
+    added=$(git status --porcelain -uno\
+                | grep ^A.*$addressbook.*vcf$\
+                | cut -b 4-\
+                | xargs -L1 grep ^FN:\
+                | cut -b 4-\
+                | sort)
+    modified=$(git status --porcelain -uno\
+                | grep ^M.*$addressbook.*vcf$\
+                | cut -b 4-\
+                | xargs -L1 grep ^FN:\
+                | cut -b 4-\
+                | sort)
+    deleted=$(git status --porcelain -uno\
+                | grep ^D.*$addressbook.*vcf$\
+                | cut -b 4-\
+                | xargs --replace={} git diff --cached -- {}\
+                | grep ^-FN:\
+                | cut -b 5-\
+                | sort)
+
+    if [ -n "$added" -o -n "$modified" -o -n "$deleted" ]; then
+        echo "$user: $addressbookTitle"
+        echo "---------"
+
+        if [ -n "$added" ]; then
+            echo "Added:"
+            echo "$added" | sed 's/^/- /'
+            echo
+        fi
+
+        if [ -n "$modified" ]; then
+            echo "Modified:"
+            echo "$modified" | sed 's/^/- /'
+            echo
+        fi
+
+        if [ -n "$deleted" ]; then
+            echo "Deleted:"
+            echo "$deleted" | sed 's/^/- /'
+            echo
+        fi
+    fi
+done
diff --git a/vdirsyncer-config.example b/vdirsyncer-config.example
new file mode 100644 (file)
index 0000000..e9e6dc6
--- /dev/null
@@ -0,0 +1,25 @@
+[general]
+status_path = "./vdirsyncer-status/"
+
+[pair anna]
+a = "remote__anna"
+b = "local__anna"
+#collections = ["from a", "from b"]
+#collections = ["from a"]
+collections = ["contacts", "geburtstagsliste"]
+conflict_resolution = "a wins"
+metadata = ["displayname"]
+
+
+[storage local__anna]
+type = "filesystem"
+path = "./anna/"
+fileext = ".vcf"
+
+
+[storage remote_anna]
+type = "carddav"
+url = "https://cloud.example.org/remote.php/carddav/"
+username = "[email protected]"
+password.fetch = ["command", "cat", "./.password-anna"]
+read_only = true