#!/bin/sh # TODO: Guix pull on commit specified in package-lists/guix-version? # TODO: On foreign distro, use local package manager to install Guix. (Start with pacman.) # TODO: Make functions to make it easier to re-order calls? HTTPS_ROOT=https://gitlab.com/ SSH_ROOT=git@gitlab.com: ROOT=$HTTPS_ROOT PROFILE=/tmp/homeinit-$USER/homeinit USER_NAME=Ambrevar [ -z "$SOURCEDIR" ] && SOURCEDIR="$HOME/personal" [ -z "$XDG_CONFIG_HOME" ] && XDG_CONFIG_HOME="$HOME/.config" [ -z "$XDG_DATA_HOME" ] && XDG_DATA_HOME="$HOME/.local/share" [ -z "$EMACS_MIN_VERSION" ] && EMACS_MIN_VERSION=26 usage() { cat <&2 Usage: ${0##*/} Initialize user profile: install packages, set up folders, etc. Options: -g DEVICE: Device where to sync ~/.gnupg. -u: Skip large updates (packages, emails, etc.) Environment variables: SOURCEDIR=$SOURCEDIR XDG_CONFIG_HOME=$XDG_CONFIG_HOME XDG_DATA_HOME=$XDG_DATA_HOME EMACS_MIN_VERSION=$EMACS_MIN_VERSION Direct link: ${HTTPS_ROOT}ambrevar/dotfiles/raw/master/.local/bin/homeinit EOF exit } OPT_UPDATE=true OPT_DEVICE="" while getopts ":hg:u" opt; do case $opt in h) usage exit ;; g) OPT_DEVICE="$OPTARG" ;; u) OPT_UPDATE=true ;; \?) usage exit 1 ;; esac done shift $(($OPTIND - 1)) RED='\033[0;31m\033[1m' GREEN='\033[0;32m\033[1m' YELLOW='\033[0;33m\033[1m' BOLD='\033[0m\033[1m' NORMAL='\033[0m' section() { echo -e "$GREEN==> $@$NORMAL" } message() { echo -e "$YELLOW:: $@$NORMAL" } info() { echo -e "$@" } warning() { echo -e "${YELLOW}WARNING:: $@$NORMAL" } ## "ln" wrapper. ## If $2 is a folder, create the link in it using the basename of $1. ## Existing files are no clobbered, unless they already are a symlink. lnn() { # $1=TARGET $2=LINK|FOLDER if [ -d "$2" ]; then set -- "$1" "$2/$(basename "$1")" fi if [ ! -e "$2" ] || [ -h "$2" ]; then ln -svnf "$1" "$2" fi } inpath() { for i; do if ! command -v "$i" >/dev/null 2>&1; then return 1 fi done return 0 } ################################################################################ # section "Initial packages" if inpath guix; then mkdir -p ~/.config/guix ## OpenSSH is not an input of Git, upstream does not want to increase the ## closure size that much. mkdir -p "$(dirname "$PROFILE")" guix package --profile="$PROFILE" --install openssh gnupg git stow password-store pinentry-tty pinentry-gtk2 cryptsetup source "$PROFILE"/etc/profile fi if [ ! -d ~/.gnupg ]; then section "GPG sync" if [ -n "$OPT_DEVICE" ]; then section "GnuPG" sudo cryptsetup open "$OPT_DEVICE" gpg_backup sudo mount -o compress=zstd /dev/mapper/gpg_backup /mnt cp -a /mnt/public/.gnupg ~/ sudo umount /mnt sudo cryptsetup close gpg_backup else warning "No device specified." fi fi section "GPG" if [ -d ~/.gnupg ]; then ROOT=$SSH_ROOT ## Set up gpg-agent to authenticate to SSH_ROOT. chmod -R go-rwx ~/.gnupg export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket) if [ "$GPG_TTY" != "not a tty" ]; then ## If a TTY, since our ~/.gnupg/gpg-agent.conf exists and specifies a ## pinentry, we must force the TTY version or else it won't work from a TTY. gpgconf --kill gpg-agent cat<"$(dirname "$PROFILE")/gpg-agent.conf" ## 1-day timeout default-cache-ttl 86400 max-cache-ttl 86400 ## SSH enable-ssh-support default-cache-ttl-ssh 86400 max-cache-ttl-ssh 86400 ## Force pinentry (should be pinentry-tty) pinentry-program $(readlink -f $(which pinentry)) EOF gpg-agent --homedir ~/.gnupg --daemon --options "$(dirname "$PROFILE")/gpg-agent.conf" fi ## Start gpg-agent manually since SSH requests do not do it automatically. gpg-connect-agent updatestartuptty /bye if [ -e "$SOURCEDIR" ]; then git -C "$SOURCEDIR" pull else git clone ${SSH_ROOT}$USER_NAME/personal ~/personal fi if [ -e ~/.password-store ]; then git -C ~/.password-store pull else git clone ${SSH_ROOT}$USER_NAME/password-store ~/.password-store ## The following is necessary to make sure the 'diff' GPG filter is properly set up. pass git init fi else warning "~/.gnupg not found." fi section "Persistent folders" for i in .cache/mail .config .config/guix .config/transmission-daemon .emacs.d .local/share .mpv projects temp; do mkdir -pv "$HOME/$i" done section "dotfiles" if [ -e ~/dotfiles ]; then git -C ~/dotfiles remote set-url origin ${ROOT}$USER_NAME/dotfiles git -C ~/dotfiles pull else git clone ${ROOT}$USER_NAME/dotfiles ~/dotfiles || exit 1 fi pushd ~/dotfiles ## .bash_profile may prevent .profile from being parsed, so we move it. [ -e ~/.bash_profile ] && mv -v ~/.bash_profile ~/.bash_profile.old stow -v . || exit 1 popd section "System packages" if inpath guix; then if [ ! -e ~/.cache/guix/pull ]; then message "Update Guix to yesterday's version" ## TODO: This clones the repository twice, which is a bit dumb, but cloning ## locally and then running `guix pull --url=/tmp/guix` produces a different ## hash in ~/.cache/guix/... git clone https://git.savannah.gnu.org/git/guix.git /tmp/guix YESTERDAY_COMMIT=$(git -C /tmp/guix log --until=yesterday -n 1 --format=%H) guix pull --commit=$YESTERDAY_COMMIT hash guix unset YESTERDAY_COMMIT rm -rf /tmp/guix elif $OPT_UPDATE; then CURRENT_COMMIT=$(guix --version 2>/dev/null | awk '{print $4; exit}') for i in $HOME/.cache/guix/pull/*; do if [ "$(git -C "$i" config --get remote.origin.url)" = "https://git.savannah.gnu.org/git/guix.git" ]; then REPO="$i" break fi done CURRENT_COMMIT_DATE=$(git -C "$REPO" log -n 1 --format=%ct $CURRENT_COMMIT) git -C $REPO pull hash guix LATEST_COMMIT_DATE=$(git -C "$REPO" log -n 1 --format=%ct) [ -z "$GUIX_DISTRO_AGE_WARNING" ] && GUIX_DISTRO_AGE_WARNING=$((7 * 24 * 60 * 60)) info "Current commit date: $(date --date="@$CURRENT_COMMIT_DATE") ($CURRENT_COMMIT_DATE)" info "Last commit date: $(date --date="@$LATEST_COMMIT_DATE") ($LATEST_COMMIT_DATE)" info "Difference (in seconds): $(($LATEST_COMMIT_DATE - $CURRENT_COMMIT_DATE))" if [ $(($LATEST_COMMIT_DATE - $CURRENT_COMMIT_DATE)) -gt $GUIX_DISTRO_AGE_WARNING ]; then message "Update Guix since it's older than GUIX_DISTRO_AGE_WARNING ($GUIX_DISTRO_AGE_WARNING)." YESTERDAY_COMMIT=$(git -C "$REPO" log --until=yesterday -n 1 --format=%H) guix pull --commit=$YESTERDAY_COMMIT unset YESTERDAY_COMMIT fi unset LATEST_COMMIT_DATE unset CURRENT_COMMIT_DATE unset REPO unset CURRENT_COMMIT fi ## TODO: The manifest will uninstall other unspecified packages. Is this ## really what we want? # export GUIX_PACKAGE_PATH=$HOME/.guix-packages ~/.config/guix/current/bin/guix package --manifest=$HOME/.package-lists/guix-manifest.scm --keep-failed . ~/.guix-profile/etc/profile fi section "Quicklisp" QUICKLISP_DIR=$HOME/.quicklisp QUICKLISP_URL=https://beta.quicklisp.org/quicklisp.lisp DOWNLOAD_AGENT=curl DOWNLOAD_AGENT_FLAGS=--output LISP=sbcl LISP_FLAGS=--no-userinit if [ ! -d "$QUICKLISP_DIR" ] && inpath $LISP && inpath $DOWNLOAD_AGENT; then $DOWNLOAD_AGENT $DOWNLOAD_AGENT_FLAGS /tmp/setup.lisp $QUICKLISP_URL $LISP $LISP_FLAGS \ --eval '(require "asdf")' \ --load /tmp/setup.lisp \ --eval '(quicklisp-quickstart:install :path "'$QUICKLISP_DIR'/")' \ --eval '(uiop:quit)' rm -v /tmp/setup.lisp fi section "Bookmarks" if inpath qutebrowser; then mkdir -pv "$XDG_CONFIG_HOME/qutebrowser/bookmarks" lnn "$SOURCEDIR/bookmarks/bookmarks" "$XDG_CONFIG_HOME/qutebrowser/bookmarks/urls" lnn "$SOURCEDIR/bookmarks/quickmarks" "$XDG_CONFIG_HOME/qutebrowser/" fi section "Emacs" if ! inpath emacs; then message "Emacs: Installing latest development version" pushd "$HOME/projects" [ -e emacs ] || git clone https://git.savannah.gnu.org/git/emacs.git mkdir -pv ../emacs-build cd ../emacs-build ../emacs/configure \ --disable-gtk-deprecation-warnings \ --without-pop \ --without-kerberos \ --without-kerberos5 \ --with-x-toolkit=gtk3 \ --with-jpeg \ --with-tiff \ --with-gif \ --with-png \ --with-rsvg \ --with-xml2 \ --with-imagemagick \ --with-xft \ --with-libotf \ --without-gsettings \ --without-gconf \ --with-gnutls \ --with-modules \ --with-threads sudo make install popd fi if inpath emacs; then message "Emacs cache folder" mkdir -pv "$HOME/.cache/emacs/" if ! inpath guix; then if $OPT_UPDATE; then message "Emacs ELPA packages" yes | emacs --batch -l ~/.emacs.d/init.el --eval '(progn (package-refresh-contents) (package-install-selected-packages))' fi fi message "Emacs local packages" if [ -e ~/.local/share/emacs/site-lisp ]; then for i in ~/.local/share/emacs/site-lisp/*; do echo "$i" git -C "$i" pull done else mkdir -pv ~/.local/share/emacs/site-lisp fi fi if inpath guix; then section "Cleanup initial packages" rm -rv "$(dirname "$PROFILE")" fi if [ -x ~/.local/bin/updatedb-local ]; then section "locate db" ~/.local/bin/updatedb-local fi if [ -f ~/personal/history/eshell.gpg ]; then section "Shell history" mkdir -pv ~/.cache/emacs/eshell/ gpg --output ~/.cache/emacs/eshell/history --decrypt ~/personal/history/eshell.gpg fi section "Mail" # lnn "$SOURCEDIR/mail/authinfo.gpg" "$HOME/.authinfo.gpg" ## Only if no using password-store. mkdir -pv "$HOME/.cache/mail/" while IFS= read -r i; do ## Warning: We need to eval here to expand the "~". mkdir -pv $(eval echo $i) done <