From 638c907c0696b0333b6ea155a098bfcfed9f6709 Mon Sep 17 00:00:00 2001 From: Andrejus Date: Tue, 3 Mar 2020 00:05:41 +0000 Subject: [PATCH] further cleanup update docs and tests --- .github/workflows/main.yml | 12 +++ Dockerfile | 9 +- Makefile | 15 ++-- README.md | 2 +- bash/.bashrc | 103 +++++++++++++++++++++++ bootstrap.sh | 45 +++++----- fish/config.fish | 26 +++++- fish/functions/update.fish | 3 + install.sh | 11 ++- install/00-apt.sh | 2 +- install/02-fish.sh | 5 +- install/10-pyenv.sh | 1 - install/20-nvm.sh | 24 ++++++ install/{20-docker.sh => 30-docker.sh} | 0 install/{21-keybase.sh => 31-keybase.sh} | 4 +- tests/Makefile | 4 +- tests/test_binaries.py | 68 ++++++++++----- 17 files changed, 266 insertions(+), 68 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 fish/functions/update.fish create mode 100755 install/20-nvm.sh rename install/{20-docker.sh => 30-docker.sh} (100%) rename install/{21-keybase.sh => 31-keybase.sh} (83%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..0c92f61 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,12 @@ +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + name: + steps: + - uses: actions/checkout@v2 + - name: Build Docker image + run: make build + - name: Test Docker image + run: make test diff --git a/Dockerfile b/Dockerfile index ba09104..afce590 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,18 +26,19 @@ RUN echo "$USER ALL=(ALL) NOPASSWD: ALL" \ # Filesystem copy steps # ---------------------------------------------------------------------------- # -ADD --chown=test-user . "$WORKSPACE/dotfiles" WORKDIR "$WORKSPACE/dotfiles" +ADD --chown=test-user . . +USER test-user # ---------------------------------------------------------------------------- # # Install steps # ---------------------------------------------------------------------------- # -USER test-user -RUN make install +RUN make # ---------------------------------------------------------------------------- # # Test steps # ---------------------------------------------------------------------------- # -CMD ["cd", "tests", "&&", "make"] +WORKDIR "$WORKSPACE/dotfiles/tests" +ENTRYPOINT ["make"] diff --git a/Makefile b/Makefile index 9ff64da..ecc0db5 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,10 @@ # ---------------------------------------------------------------------------- # # Local target commands (warning: affects local environment) # ---------------------------------------------------------------------------- # -.PHONY: install clean +.PHONY: clean # Install dotfiles locally -install: +all: ./bootstrap.sh # Clean up after install @@ -15,16 +15,17 @@ clean: # ---------------------------------------------------------------------------- # # Docker commands # ---------------------------------------------------------------------------- # -.PHONY: build run use +.PHONY: build test start # Build and tag docker image build: - docker build . -t dotfiles + docker build . -t dotfiles --build-arg -# Run tests in docker container -run: +# Run tests in docker container (args to specify test) +test: + docker build . -t dotfiles --build-args docker run dotfiles # Launch bash in docker container -use: +start: docker run -it dotfiles /bin/bash diff --git a/README.md b/README.md index 0324df7..a35a752 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Collection of tracked dotfiles and supporting install scripts. Tested on and compatible with: - * Ubuntu 19.04 + * Ubuntu 18.04 ## Install diff --git a/bash/.bashrc b/bash/.bashrc index 0f350c1..2d8ab8e 100644 --- a/bash/.bashrc +++ b/bash/.bashrc @@ -1,3 +1,13 @@ +# ---------------------------------------------------------------------------- # +# Cross-shell +# ---------------------------------------------------------------------------- # + +# config +XDG_CONFIG_HOME="$HOME/.config" + +# workspace +WORKSPACE="$HOME/workspace" + # .local export PATH="$HOME/.local/bin:$PATH" @@ -5,7 +15,100 @@ export PATH="$HOME/.local/bin:$PATH" PYENV_ROOT="$HOME/.pyenv" export PATH="$PYENV_ROOT/bin:$PATH" export PATH="$PYENV_ROOT/shims:$PATH" +[ -x "$(command -v pyenv)" ] && eval "$(pyenv init -)" # poetry POETRY_ROOT="$HOME/.poetry" export PATH="$POETRY_ROOT/bin:$PATH" + +# nvm +NVM_DIR="$HOME/.nvm" +export PATH="$NVM_DIR/bin:$PATH" +[ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" +[ -s "$NVM_DIR/bash_completion" ] && source "$NVM_DIR/bash_completion" + + +# ---------------------------------------------------------------------------- # +# Bash specific +# ---------------------------------------------------------------------------- # + +# set a fancy prompt (non-color, unless we know we "want" color) +case "$TERM" in + xterm-color|*-256color) color_prompt=yes;; +esac + +# uncomment for a colored prompt, if the terminal has the capability; turned +# off by default to not distract the user: the focus in a terminal window +# should be on the output of commands, not on the prompt +force_color_prompt=yes + +if [ -n "$force_color_prompt" ]; then + if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then + # We have color support; assume it's compliant with Ecma-48 + # (ISO/IEC-6429). (Lack of such support is extremely rare, and such + # a case would tend to support setf rather than setaf.) + color_prompt=yes + else + color_prompt= + fi +fi + +if [ "$color_prompt" = yes ]; then + PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' +else + PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' +fi +unset color_prompt force_color_prompt + +# If this is an xterm set the title to user@host:dir +case "$TERM" in +xterm*|rxvt*) + PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" + ;; +*) + ;; +esac + +# enable color support of ls and also add handy aliases +if [ -x /usr/bin/dircolors ]; then + test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" + alias ls='ls --color=auto' + #alias dir='dir --color=auto' + #alias vdir='vdir --color=auto' + + alias grep='grep --color=auto' + alias fgrep='fgrep --color=auto' + alias egrep='egrep --color=auto' +fi + +# colored GCC warnings and errors +#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + +# some more ls aliases +alias ll='ls -alF' +alias la='ls -A' +alias l='ls -CF' + +# Add an "alert" alias for long running commands. Use like so: +# sleep 10; alert +alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"' + +# Alias definitions. +# You may want to put all your additions into a separate file like +# ~/.bash_aliases, instead of adding them here directly. +# See /usr/share/doc/bash-doc/examples in the bash-doc package. + +if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases +fi + +# enable programmable completion features (you don't need to enable +# this, if it's already enabled in /etc/bash.bashrc and /etc/profile +# sources /etc/bash.bashrc). +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi diff --git a/bootstrap.sh b/bootstrap.sh index 06db354..24781eb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,33 +1,38 @@ #!/usr/bin/env bash # -# Script to set up dotfiles repository and run installer. +# Script to set up and run dotfiles installer # -# Installs git using apt-get if not in $PATH. -# Pulls latest dotfiles repository. +# Installs git using `apt-get` if not in $PATH +# step may be skipped, +# @see $FAST_MODE +# +# Pulls latest target repository using git +# @see $REPOSITORY +# +# Creates workspace if one doesn't already exist +# @see $WORKSPACE +# +# Runs installer +# @see $INSTALLER # # -# Usage: +# Usage # -# i. Run script. +# i. Run script # -# $ bash bootstrap.sh -# $ bash /path/to/bootstrap.sh -# $ wget path.to/bootstrap.sh -qO - | bash +# $ ./bootstrap.sh +# $ /path/to/bootstrap.sh # -# ii. Run explicitly in a bash shell. +# ii. Download and run script # -# $ bash bootstrap.sh -# $ bash /path/to/bootstrap.sh -# $ wget path.to/bootstrap.sh -qO - | bash +# a. non-interactively +# $ wget path.to/bootstrap.sh -qO - | bash # -# iii. Source into existing shell. -# -# $ source bootstrap.sh -# $ source /path/to/bootstrap.sh -# $ source <(wget -q path.to/bootstrap.sh) +# b. interactively +# $ bash <(wget -qO- path.to/bootstrap.sh) # # -# Configuration: +# Configuration # # $REPOSITORY - GitHub repository to clone # @default "andrejusk/dotfiles" @@ -39,7 +44,7 @@ # @default "install.sh" # # $FAST_MODE - whether to skip git (and speed up install steps) -# @defualt unset, i.e. false +# @defualt false # # set -o pipefail @@ -81,7 +86,7 @@ if [ -z "$FAST_MODE" ]; then # Ensure latest is pulled echo "pulling latest..." - if [[ ! -d $dotfiles_dir ]]; then + if ! [ -d "$dotfiles_dir" ]; then mkdir -p "$dotfiles_dir" git clone -q "$repository_url" "$dotfiles_dir" else diff --git a/fish/config.fish b/fish/config.fish index ff9bcc1..97193fd 100644 --- a/fish/config.fish +++ b/fish/config.fish @@ -1,3 +1,13 @@ +# ---------------------------------------------------------------------------- # +# Cross-shell +# ---------------------------------------------------------------------------- # + +# config +set XDG_CONFIG_HOME $HOME/.config + +# workspace +set WORKSPACE $HOME/workspace + # .local set -x PATH $HOME/.local/bin $PATH @@ -5,10 +15,22 @@ set -x PATH $HOME/.local/bin $PATH set PYENV_ROOT $HOME/.pyenv set -x PATH $PYENV_ROOT/bin $PATH set -x PATH $PYENV_ROOT/shims $PATH +type -q pyenv && pyenv init - | source # poetry -set PYENV_ROOT $HOME/.pyenv -set -x PATH $PYENV_ROOT/bin $PATH +set POETRY_ROOT $HOME/.poetry +set -x PATH $POETRY_ROOT/bin $PATH + +# nvm +# set NVM_ROOT $HOME/.nvm +# set -x PATH $NVM_ROOT/bin $PATH +# +# + + +# ---------------------------------------------------------------------------- # +# Fish specific +# ---------------------------------------------------------------------------- # # Wipe greeting set fish_greeting diff --git a/fish/functions/update.fish b/fish/functions/update.fish new file mode 100644 index 0000000..e64b01e --- /dev/null +++ b/fish/functions/update.fish @@ -0,0 +1,3 @@ +function update -d 'run dotfiles install' + make --directory="$WORKSPACE/dotfiles" +end diff --git a/install.sh b/install.sh index 29a24c9..0052e50 100755 --- a/install.sh +++ b/install.sh @@ -27,13 +27,16 @@ touch "$install_lock_file" # Requires clean export install_dir="$dotfiles_dir/install" readonly script_filter="$install_dir/*.sh" # Don't escape to unwrap glob for script in $script_filter; do - - # Avoid pattern matching self [ -e "$script" ] || continue - # Log execution + # Get script name, split by dash script_name="$(basename "$script" ".sh")" - printf "\nRunning ${C_YELLOW}$script_name${C_NC}...\n${C_DGRAY}" + IFS='-' read -a script_fields <<< "$script_name" + script_number=${script_fields[0]} + script_target=${script_fields[1]} + + # Log execution + printf "\nRunning #$script_number ${C_YELLOW}$script_target${C_NC}...\n${C_DGRAY}" # Run and indent output chmod +x "$script" diff --git a/install/00-apt.sh b/install/00-apt.sh index 4b8547e..3219fd5 100755 --- a/install/00-apt.sh +++ b/install/00-apt.sh @@ -2,7 +2,7 @@ # # apt update and upgrade # -# Install list of packages in ./00-apt-pkglist +# Install list of packages in 00-apt-pkglist # # pre clean diff --git a/install/02-fish.sh b/install/02-fish.sh index 42666a6..b47e2c0 100755 --- a/install/02-fish.sh +++ b/install/02-fish.sh @@ -39,12 +39,11 @@ printf "fish is default login shell\n" # 3. fish dotfiles are symlinked fish_source="$dotfiles_dir/fish" -fish_target="$HOME/.config/fish" +fish_target="$XDG_CONFIG_HOME/fish" link_folder "$fish_source" "$fish_target" printf "fish dotfiles linked\n" # 4. fisher is installed -XDG_CONFIG_HOME="$HOME/.config" fisher_location="$XDG_CONFIG_HOME/fish/functions/fisher.fish" if ! [ -f "$fisher_location" ]; then @@ -57,5 +56,3 @@ fi printf "fisher is installed, updating...\n" fish -c "fisher" fish -c "fisher --version" - -export XDG_CONFIG_HOME diff --git a/install/10-pyenv.sh b/install/10-pyenv.sh index 2d4465c..00d8522 100755 --- a/install/10-pyenv.sh +++ b/install/10-pyenv.sh @@ -5,7 +5,6 @@ # # 1. pyenv is installed -export PYENV_ROOT="$HOME/.pyenv" if not_installed "pyenv"; then printf "Installing pyenv...\n" diff --git a/install/20-nvm.sh b/install/20-nvm.sh new file mode 100755 index 0000000..c75dde7 --- /dev/null +++ b/install/20-nvm.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# After running this script: +# 1. nvm is installed +# + +# 1. nvm is installed +if not_installed "nvm"; then + + printf "Installing nvm...\n" + + # Install nvm + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash + refresh + +fi +printf "nvm is installed, upgrading...\n" +git --git-dir="$NVM_DIR/.git" pull +nvm update --lts node +nvm update node +nvm update npm +nvm --version +node --version +npm --version diff --git a/install/20-docker.sh b/install/30-docker.sh similarity index 100% rename from install/20-docker.sh rename to install/30-docker.sh diff --git a/install/21-keybase.sh b/install/31-keybase.sh similarity index 83% rename from install/21-keybase.sh rename to install/31-keybase.sh index 85431ee..ed67c4b 100755 --- a/install/21-keybase.sh +++ b/install/31-keybase.sh @@ -10,8 +10,8 @@ if not_installed "keybase"; then printf "Installing keybase...\n" curl --remote-name https://prerelease.keybase.io/keybase_amd64.deb - install ./keybase_amd64.deb - rm ./keybase_amd64.deb + install keybase_amd64.deb + rm keybase_amd64.deb fi printf "keybase is installed\n" diff --git a/tests/Makefile b/tests/Makefile index 48cd8c2..b133a97 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,6 +1,6 @@ # dotfiles tests Makefile -all: SHELL:=/bin/bash +SHELL := /bin/bash all: - source ~/.bashrc + source ${HOME}/.bashrc poetry install poetry run pytest diff --git a/tests/test_binaries.py b/tests/test_binaries.py index d811f9a..276c645 100644 --- a/tests/test_binaries.py +++ b/tests/test_binaries.py @@ -1,17 +1,45 @@ #!/usr/bin/env python3 # -# Verifies dotfiles install +# Verifies dotfiles installed binaries correctly # from distutils.spawn import find_executable from typing import List +from subprocess import run, CalledProcessError import pytest -# List of binaries to test +# ---------------------------------------------------------------------------- # +# Helper functions +# ---------------------------------------------------------------------------- # +def in_path(binary: str) -> bool: + """ + Helper function to check whether `binary` is in PATH. + """ + return find_executable(binary) is not None + +def in_shell_path(shell: str, binary: str) -> bool: + """ + Helper function to check whether `binary` is in interactive shell's PATH. + """ + command = "{0} -i -c 'command -v {1}'".format(shell, binary) + try: + result = run(command, shell=True) + return (result.returncode == 0) + except: + return False + + +# ---------------------------------------------------------------------------- # +# Test fixtures +# ---------------------------------------------------------------------------- # +shells: List[str] = [ + 'sh', 'bash', 'fish', +] + binaries: List[str] = [ - # shells - "sh", "bash", "fish", + # extend shells + *shells, # tools "git", @@ -19,25 +47,25 @@ binaries: List[str] = [ "docker", "docker-compose", "screenfetch", - # languages - "pyenv", - "python3", - "poetry", + # language: python + "pyenv", "python3", "pip3", "poetry", + + # langauge: node + "nvm", "node", "npm", "yarn", ] -def binary_in_path(binary: str) -> bool: - """ - Helper function to check whether `binary` is in PATH. - """ - - return find_executable(binary) is not None - +# ---------------------------------------------------------------------------- # +# Tests +# ---------------------------------------------------------------------------- # +@pytest.mark.parametrize("shell", shells) +def test_shells(shell: str): + """ Assert all shells we expect are in PATH. """ + assert in_path(shell) @pytest.mark.parametrize("binary", binaries) -def test_binaries(binary: str): - """ - Asserts all binaries are in PATH. - """ - assert binary_in_path(binary) +@pytest.mark.parametrize("shell", shells) +def test_binaries(shell: str, binary: str): + """ Asserts all binaries are in all shells. """ + assert in_shell_path(shell, binary)