From 8bd5291eb072526adc9fd11f85824ed86d5ba15c Mon Sep 17 00:00:00 2001 From: Benedetto Polimeni Date: Fri, 20 Sep 2024 07:46:58 +0000 Subject: [PATCH] init --- .Rprofile | 70 ++++++++++++++++++++ .condarc | 6 ++ .condaxrc | 6 ++ .config/shell/aliases.sh | 55 ++++++++++++++++ .config/shell/functions.sh | 108 +++++++++++++++++++++++++++++++ .config/shell/functions.zsh | 30 +++++++++ .config/shell/fzf-completions.sh | 24 +++++++ .config/tmux/clear_jobs.sh | 5 ++ .config/tmux/slurm_jobs.sh | 20 ++++++ .tmux.conf | 68 +++++++++++++++++++ .zshenv | 8 +++ .zshrc | 77 ++++++++++++++++++++++ LICENSE | 24 +++++++ README.md | 29 +++++++++ bootstrap | 29 +++++++++ 15 files changed, 559 insertions(+) create mode 100644 .Rprofile create mode 100644 .condarc create mode 100644 .condaxrc create mode 100644 .config/shell/aliases.sh create mode 100644 .config/shell/functions.sh create mode 100644 .config/shell/functions.zsh create mode 100644 .config/shell/fzf-completions.sh create mode 100755 .config/tmux/clear_jobs.sh create mode 100755 .config/tmux/slurm_jobs.sh create mode 100644 .tmux.conf create mode 100644 .zshenv create mode 100644 .zshrc create mode 100644 LICENSE create mode 100644 README.md create mode 100755 bootstrap diff --git a/.Rprofile b/.Rprofile new file mode 100644 index 0000000..6ec79f3 --- /dev/null +++ b/.Rprofile @@ -0,0 +1,70 @@ +# vim: set filetype=r: + +# Default CRAN mirror +options(repos=structure(c(CRAN="https://cloud.r-project.org/"))) + +# Print data.table's columns classes and key +options(datatable.print.class = TRUE, datatable.print.keys = TRUE) + +# Monitor memory usage ( https://stackoverflow.com/questions/1358003 ) +.ls.objects <- function (pos = 1, + pattern, + order.by, + decreasing=FALSE, + head=FALSE, + n=5) { + napply <- function(names, fn) sapply(names, function(x) fn(get(x, pos = pos))) + names <- ls(pos = pos, pattern = pattern) + obj.class <- napply(names, function(x) as.character(class(x))[1]) + obj.mode <- napply(names, mode) + obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class) + obj.prettysize <- napply(names, + function(x) { + format(utils::object.size(x), units = "auto") + }) + obj.size <- napply(names, object.size) + obj.dim <- t(napply(names, function(x) as.numeric(dim(x))[1:2])) + vec <- is.na(obj.dim)[, 1] & (obj.type != "function") + obj.dim[vec, 1] <- napply(names, length)[vec] + out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim) + names(out) <- c("Type", "Size", "PrettySize", "Length/Rows", "Columns") + if (!missing(order.by)) { + out <- out[order(out[[order.by]], decreasing=decreasing), ] + } + if (head) { + out <- head(out, n) + } + return(out) +} +.lsos <- function(..., n=10) { + .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n) +} + +# Built-in color names ( http://www.sthda.com/english/wiki/colors-in-r ) +.showCols <- function(cl=colors(), + bg = "grey", + cex = 0.75, + rot = 30) { + m <- ceiling(sqrt(n <-length(cl))) + length(cl) <- m*m; cm <- matrix(cl, m) + require("grid") + grid.newpage(); vp <- viewport(w = .92, h = .92) + grid.rect(gp=gpar(fill=bg)) + grid.text(cm, x = col(cm)/m, y = rev(row(cm))/m, rot = rot, vp=vp, + gp=gpar(cex = cex, col = cm)) +} + +# Set IRkernel default plot size ( https://irkernel.github.io/ ) +.d <- function(width = 8, height = 5, scale = 2) { + options(repr.plot.width = width, repr.plot.height = height, + jupyter.plot_scale = scale) +} + +# Initialize environment and print summary, useful for notebooks +.envInit <- function(packages = NULL) { + if (!is.null(packages)) { + invisible(lapply(packages, library, character.only = TRUE)) + } + print(R.version.string) + print(sapply(packages, function(x) paste0(packageVersion(x)))) +} diff --git a/.condarc b/.condarc new file mode 100644 index 0000000..2d5fdaf --- /dev/null +++ b/.condarc @@ -0,0 +1,6 @@ +channels: + - conda-forge + - bioconda + - nodefaults +channel_priority: strict +auto_activate_base: false diff --git a/.condaxrc b/.condaxrc new file mode 100644 index 0000000..7d859cf --- /dev/null +++ b/.condaxrc @@ -0,0 +1,6 @@ +prefix_path: "~/.local/share/condax" +link_destination: "~/.local/bin" +channels: + - conda-forge + - bioconda + - nodefaults diff --git a/.config/shell/aliases.sh b/.config/shell/aliases.sh new file mode 100644 index 0000000..a77a7e7 --- /dev/null +++ b/.config/shell/aliases.sh @@ -0,0 +1,55 @@ +# Alias to manage dotfiles in a bare repo +alias config='git --git-dir=$HOME/.config/dotfiles --work-tree=$HOME' + +# Colored outputs +alias ls='ls --color=auto' +alias grep='grep --color=auto' +alias fgrep='fgrep --color=auto' +alias egrep='egrep --color=auto' + +# Safety aliases +alias rm='rm -i' +alias cp='cp -i' +alias mv='mv -i' + +# Quality-of-life aliases +[ -x "$(command -v batcat)" ] && alias bat='batcat' +alias la='ls -A' +alias l='ls -CF' +alias ll='ls -alF' +if [ ! -x "$(command -v realpath)" ] && [ -x "$(command -v readlink)" ]; then + alias realpath='readlink -f' +fi +alias fsort='LC_ALL=C sort' +[ -x "$(command -v stow)" ] && alias stow='stow --no-folding' +if [ -x "$(command -v tmux)" ]; then + tm() { + if [ -n "$1" ]; then + tmux attach-session -d -t "$1" + else + tmux attach-session -d + fi + + } + alias tmc='tmux load-buffer' + alias tmv='tmux save-buffer' +fi +if command -v micromamba &>/dev/null; then + alias c='micromamba' + ! command -v mamba &>/dev/null && alias mamba='micromamba' + ! command -v conda &>/dev/null && alias conda='micromamba' +elif command -v mamba &>/dev/null; then + alias c='mamba' +elif command -v conda &>/dev/null; then + alias c='conda' +fi +alias ca='c activate' cda='c deactivate' +[ -x "$(command -v zoxide)" ] && alias z='zi' + +# Slurm shortcuts +if [ -x "$(command -v sbatch)" ]; then + __jobcols() { printf '%s' "$((COLUMNS / 4))"; } + alias squeue='squeue -o "%.18i %.9P %.$(__jobcols)j %.8u %.2t %.10M %.6D %R"' + alias squeueu='squeue -u $USER' + alias sacct='sacct -o "jobid,jobname%$(__jobcols),alloccpus,MaxRSS,state,exitcode,Start,End"' +fi diff --git a/.config/shell/functions.sh b/.config/shell/functions.sh new file mode 100644 index 0000000..6c6e65c --- /dev/null +++ b/.config/shell/functions.sh @@ -0,0 +1,108 @@ +# Print the header (the first line of input) +# and then run the specified command on the body (the rest of the input) +# use it in a pipeline, e.g. ps | body grep somepattern +# (ref:https://unix.stackexchange.com/a/11859) +body() { + IFS= read -r header + printf '%s\n' "$header" + "$@" +} + +# Reverse complement a DNA sequence +revc() { + if [ -n "$1" ]; then + echo "$1" | rev | tr 'ACGTRYKMBDHV' 'TGCAYRMKVHDB' + else + rev | tr 'ACGTRYKMBDHV' 'TGCAYRMKVHDB' + fi +} + +# count unique lines; sortcut for `sort -u | wc -l` +wcu() { + sort -u "$1" | wc -l +} + +# Add the executed command line to output's header +cmdump() { + echo -E "# $*" + "$@" +} + +# Create directory and enter it +cmkdir() { + mkdir -p "$1" && cd "$1" || return +} + +# Check resource usage by user +userusage() { + ps aux | awk '{print $1,$3,$6,$8}' | sort -u | awk ' + BEGIN {print "USER\t%CPU\tRSS(GB)"} + { rss[$1]+=$3; cpu[$1]+=$2 } + END { for(i in rss){OFS="\t"; print i,cpu[i],rss[i]/1000000} }' +} + +# Wrapper for `zcat | head` +# usage: zhead [10] +zhead() { + if [ -z "$2" ]; then + local n=10 + else + local n="$2" + fi + zcat "$1" | head -n "$n" +} + +# Quickly remove large directories with rsync +rsyncrm() { + mkdir rsrm_empty + rsync -a --delete rsrm_empty/ "$1" + rmdir rsrm_empty "$1" +} + +# Scrape biocontainers +biocontainers() { + local url="https://quay.io/api/v1/repository/biocontainers" + local sprefix="https://depot.galaxyproject.org/singularity" + local dprefix="quay.io/biocontainers" + curl -s -X GET "$url"/"$1"/tag/ | + python3 -c "import json,sys;from datetime import datetime;\ + obj=json.load(sys.stdin);\ + print('docker_image\tsingularity_image\tlast_modified');\ + [print('$dprefix/$1:{}\t$sprefix/$1:{}\t{}'.format(\ + x['name'], x['name'], datetime.strptime(x['last_modified'], '%a, %d %b %Y %H:%M:%S -0000')\ + )) for x in obj['tags']]" +} +biocontainers_galaxy() { + local url="https://depot.galaxyproject.org/singularity/" + curl -s $url | sed 's/\r$//' | grep -v "\-$" | grep "^]*>//g' | + awk -vurl=$url '{print url""$1"\t"$2"-"$3}' +} + +# SSH to current directory +sshpwd() { + local shell=${SHELL:-bash} + case "$shell" in + */sh) + local args="" + ;; + *) + local args=${2:-"--login"} + ;; + esac + ssh -t "$1" "cd $PWD; $shell $args" +} + +# Get numbered header COLUMNS +nheader() { + head -n 1 "$1" | tr '\t' '\n' | nl +} + +# Generate duckduckgo email alias +duck() { + curl -s -H "Content-Type: application/json" \ + -H "Authorization: Bearer ${DUCK_TOKEN:-$1}" \ + -X POST \ + https://quack.duckduckgo.com/api/email/addresses | + sed 's/.*"\([^"]\+\)"}/\1@duck.com\n/' +} diff --git a/.config/shell/functions.zsh b/.config/shell/functions.zsh new file mode 100644 index 0000000..68fe014 --- /dev/null +++ b/.config/shell/functions.zsh @@ -0,0 +1,30 @@ +# plugins management, edited from https://github.com/mattmc3/zsh_unplugged +plugin-load() { + local repo plugdir initfiles zpd=() + local ZPLUGDIRS=( + "${XDG_DATA_HOME:-$HOME/.local/share}/zsh/plugins" + '/usr/local/share/zsh/plugins' + '/usr/share/zsh/plugins' + ) + for repo in $@; do + for zpd in $ZPLUGDIRS; do + plugdir="$zpd/${repo:t}" + if [[ -d "$plugdir" ]]; then + break + fi + done + if [[ ! -d "$plugdir" ]]; then + printf "Clone $repo ? [yN]" + if read -q; then + git clone -q --depth 1 --recursive --shallow-submodules \ + https://github.com/"$repo" "$plugdir" + else + continue + fi + fi + initfiles=($plugdir/*.{plugin.zsh,zsh-theme,zsh,sh}(N)) + (( $#initfiles )) || {echo >&2 "No init file found '$repo'." && continue} + source $initfiles[1] + done +} + diff --git a/.config/shell/fzf-completions.sh b/.config/shell/fzf-completions.sh new file mode 100644 index 0000000..818d443 --- /dev/null +++ b/.config/shell/fzf-completions.sh @@ -0,0 +1,24 @@ +# Run only if fzf has been initialized +if ! declare -F _fzf_complete &>/dev/null; then + return 0 +fi + +# Slurm process IDs +_fzf_complete_scancel() { + _fzf_complete --multi --header-lines=1 -- "$@" < <(squeue -u "$USER") +} +_fzf_complete_scancel_post() { + awk '{print $1}' +} + +_fzf_complete_scontrol() { + _fzf_complete --header-lines=1 -- "$@" < <(squeue) +} +_fzf_complete_scontrol_post() { + awk '{print $1}' +} + +if [ -n "$BASH" ]; then + complete -F _fzf_complete_scancel -o default -o bashdefault scancel + complete -F _fzf_complete_scontrol -o default -o bashdefault scontrol +fi diff --git a/.config/tmux/clear_jobs.sh b/.config/tmux/clear_jobs.sh new file mode 100755 index 0000000..f5792eb --- /dev/null +++ b/.config/tmux/clear_jobs.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +command sacct -n -o 'jobid,state' -S 'now-24hours' \ + | grep -vw RUNNING | grep -vw PENDING \ + | cut -f1 -d' ' > ~/.config/tmux/blacklist diff --git a/.config/tmux/slurm_jobs.sh b/.config/tmux/slurm_jobs.sh new file mode 100755 index 0000000..6524aea --- /dev/null +++ b/.config/tmux/slurm_jobs.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +if [ ! -f ~/.config/tmux/blacklist ]; then + touch ~/.config/tmux/blacklist +fi + +if [ -x "$(command -v sacct)" ]; then + cat <(command sacct -n -o 'jobid,state' -S 'now-24hours') \ + | sed 's/ \+/ /' | sed -r 's/^([0-9]+)\.[^ ]+ /\1 /' | sort -u \ + | grep -vwf ~/.config/tmux/blacklist \ + | awk '$1!~/[0-9]+\.b/ && $2!~/CANCELLED/ { + a[substr($2,1,1)]+=1;next + } END { + outline=""; + for(i in a) outline=outline""i":"a[i]" "; + sub(/ $/,"",outline); + if(outline) print " | "outline + }' +fi + diff --git a/.tmux.conf b/.tmux.conf new file mode 100644 index 0000000..dcd1ea5 --- /dev/null +++ b/.tmux.conf @@ -0,0 +1,68 @@ +# +# ~/.tmux.conf +# + +# prefix +unbind-key C-b +set-option -g prefix C-a +bind-key a send-prefix + +# basic options +set-window-option -g automatic-rename +set-option -g base-index 1 +set-option -g default-terminal "screen-256color" +set-option -g display-time 4000 +set-option -sg escape-time 0 +set-option -g focus-events on +set-option -g history-limit 50000 +set-option -wg mode-keys vi +set-option -g mouse on +set-option -wg pane-base-index 1 +set-option -g status-interval 5 +set-option -g renumber-windows on + +# reload tmux config +bind-key r source-file ~/.tmux.conf + +# switch window +bind-key c new-window -c "#{pane_current_path}" +bind-key C-a last-window + +# split window +unbind-key '"' +bind-key '\' split-window -h -c "#{pane_current_path}" +bind-key | split-window -hf -c "#{pane_current_path}" +unbind-key % +bind-key - split-window -v -c "#{pane_current_path}" +bind-key _ split-window -vf -c "#{pane_current_path}" + +# switch pane with hjkl +bind-key h select-pane -L +bind-key j select-pane -D +bind-key k select-pane -U +bind-key l select-pane -R + +# switch pane with alt+arrows +bind-key -n M-Up select-pane -U +bind-key -n M-Left select-pane -L +bind-key -n M-Down select-pane -D +bind-key -n M-Right select-pane -R + +# status bar +set-option -g status-style fg=colour244 +set-option -g window-status-current-style fg=colour222 +set-option -g pane-border-style fg=colour240 +set-option -g pane-active-border-style fg=colour243 +set-option -g status-left '' +set-option -g status-left-length 0 +set-option -g status-right '' +set-option -g status-right-length 0 +set-option -g status-right "%a %Y-%m-%d %H:%M#(~/.config/tmux/slurm_jobs.sh)" +set-option -g status-right-length 40 + +# clear slurm job count on status bar +bind-key b run-shell "~/.config/tmux/clear_jobs.sh" + +# fix home/end keys not working in tmux sometimes +bind-key -n Home send Escape "OH" +bind-key -n End send Escape "OF" diff --git a/.zshenv b/.zshenv new file mode 100644 index 0000000..275e272 --- /dev/null +++ b/.zshenv @@ -0,0 +1,8 @@ +export LC_ALL=C.UTF-8 LANG=C.UTF-8 +export HISTFILE=~/.zsh_history +export HISTTIMEFORMAT="%Y/%m/%d %H:%M:%S: " +export HISTSIZE=50000 +export SAVEHIST=50000 +export WORDCHARS='*?!_-.~=/&;|#$%^()[]{}<>' +export PATH="$HOME/.local/bin:$PATH" +export LD_LIBRARY_PATH="$HOME/.local/lib:$LD_LIBRARY_PATH" diff --git a/.zshrc b/.zshrc new file mode 100644 index 0000000..c8d5f4a --- /dev/null +++ b/.zshrc @@ -0,0 +1,77 @@ +# _____ _ +# |__ /___| |__ _ __ ___ +# / // __| '_ \| '__/ __| +# / /_\__ \ | | | | | (__ +# /____|___/_| |_|_| \___| +# vim: ts=2 sts=2 sw=2 et + +setopt PROMPT_SUBST +setopt INC_APPEND_HISTORY +setopt HIST_IGNORE_ALL_DUPS +setopt HIST_IGNORE_SPACE +setopt HIST_REDUCE_BLANKS +setopt NO_CASE_GLOB +setopt INTERACTIVE_COMMENTS + +autoload -Uz vcs_info +precmd() { vcs_info } +zstyle ':vcs_info:git:*' formats '%b ' +PROMPT='%(1j.[%j] .)'\ +"$([ -n "$SSH_TTY" ] && echo '%F{yellow}%M%F{white}:' ||:)"\ +'%F{cyan}%2c %F{magenta}${vcs_info_msg_0_}%(?.%F{green}.%F{red})%\▸%f ' + +bindkey '\e[H' beginning-of-line +bindkey '^[OH' beginning-of-line +bindkey '\e[1~' beginning-of-line +bindkey '\e[7~' beginning-of-line +bindkey '\e[F' end-of-line +bindkey '^[OF' end-of-line +bindkey '\e[4~' end-of-line +bindkey '\e[8~' end-of-line +bindkey '\e[3~' delete-char +bindkey '\e[1;5D' backward-word +bindkey '\e[1;5C' forward-word + +fpath+="${XDG_DATA_HOME:-$HOME/.local/share}/zsh/site-functions" +autoload -Uz compinit +compinit +zstyle ':completion:*' special-dirs true + +# Set LS_COLORS +[ -x "$(command -v dircolors)" ] && eval "$(dircolors -b)" + +# Enable command-not-found, if present and not already enabled +if ! typeset -f command_not_found_handler &>/dev/null; then + if [ -f /usr/share/doc/pkgfile/command-not-found.zsh ]; then + source /usr/share/doc/pkgfile/command-not-found.zsh + elif [ -f /etc/zsh_command_not_found ]; then + source /etc/zsh_command_not_found + fi +fi + +# fzf - https://github.com/junegunn/fzf +[ -x "$(command -v fzf)" ] && source <(fzf --zsh) + +# zoxide - https://github.com/ajeetdsouza/zoxide +[ -x "$(command -v zoxide)" ] && eval "$(zoxide init zsh)" + +# micromamba - https://mamba.readthedocs.io +if [ -x "$(command -v micromamba)" ]; then + export MAMBA_EXE="$(command -v micromamba)" + export MAMBA_ROOT_PREFIX="${XDG_DATA_HOME:-$HOME/.local/share}/conda" + eval "$("$MAMBA_EXE" shell hook --shell zsh \ + --root-prefix "$MAMBA_ROOT_PREFIX" 2> /dev/null)" +fi + +# source other config files +for f in "${XDG_CONFIG_HOME:-$HOME/.config}"/shell/*.(sh|zsh); do + source "$f" +done +unset f + +# load plugins +plugin-load zsh-users/zsh-autosuggestions +plugin-load zsh-users/zsh-syntax-highlighting +plugin-load zsh-users/zsh-history-substring-search && \ + bindkey '^[[A' history-substring-search-up && \ + bindkey '^[[B' history-substring-search-down diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..c8e41d5 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# dotfiles + +## Install +### Quickstart +```sh +sh <(curl -s https://bepoli.github.io/dotfiles/bootstrap) +``` + +### Manual install +Install `curl` and `git`. + +Check [bootstrap](bootstrap) to find out what it does, then run it. +```sh +bootstrap [-d OUTPUT_DIRECTORY] [-r REMOTE_URL] +``` +Example: +```sh +./bootstrap -d ~/.config/dotfiles -r https://github.com/$USER/dotfiles.git +``` + +## Uninstall +Undo dotfiles checkout and restore backup +```sh +# Use "config" alias defined in bootstrap and dotfiles +config sparse-checkout set --no-cone '!/*' +shopt -s dotglob +cp ~/.config/dotfiles-backup/* ~/ +shopt -u dotglob +``` diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000..64e4d22 --- /dev/null +++ b/bootstrap @@ -0,0 +1,29 @@ +#!/bin/sh + +# parse arguments and set defaults +REPODIR="$HOME/.config/dotfiles" +REMOTE='https://github.com/bepoli/dotfiles' +while getopts 'd:r:' opt; do + case "$opt" in + 'd') + REPODIR="$OPTARG" + ;; + 'r') + REMOTE="$OPTARG" + ;; + esac +done + +# initialize dotfiles +git clone --bare "$REMOTE" "$REPODIR" +alias config='git --git-dir="$REPODIR" --work-tree="$HOME"' +config checkout +if [ $? -gt 0 ]; then + mkdir -p ${REPODIR}-backup + config checkout 2>&1 | grep -E "\s+\." | awk '{print $1}' | xargs dirname | xargs -I{} mkdir -p "$REPODIR"-backup/{} + config checkout 2>&1 | grep -E "\s+\." | awk '{print $1}' | xargs -I{} mv {} "$REPODIR"-backup/{} + config checkout +fi +config sparse-checkout set --no-cone '/**' '!bootstrap' '!README.md' '!LICENSE' +config config --local status.showUntrackedFiles no +unset REPODIR REMOTE