diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8930a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.venv diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..871f80a --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12.3 diff --git a/README.md b/README.md index 358416a..04f7451 100644 --- a/README.md +++ b/README.md @@ -4,38 +4,7 @@ Environment tweaks for everyday happiness. ## Setup -```bash -git clone git@github.com:mcataford/env.git | ./bootstrap.sh -``` - -The setup script will look for pre-existing managed blocks and will not update the file if one is found. - -### Updating existing setups - -Since the managed blocks only `source` the files in this repository, pulling in updates from remote should bring in any -new tweaks you want to apply. If changes are made to the managed blocks, you will need to first remove them from where -they live and rerun the setup script. - -## Profiles - -A profile must be provided via `$ENV_PROFILE`. This can be used by steps to determine variants of a file to copy over. -Files with profile specificity should be named `file..`. - -## Structure - -The package is structured as such: - -``` -env/ - bootstrap.sh # Adds bootstrap block to your shell's config file. - files/ - shell_extras # Functions, aliases and exports for the shell. - extras.vim # Common config for neovim. - git_config # Configuration commands for git - ... Other configuration templates -``` - -Adding code to `shell_extras` will add code that gets executed on shell-start. +Use the `bootstrap.sh` script to set up a virtual environment with Ansible and its dependencies, then `setup.sh`! ## Contributing diff --git a/bootstrap.sh b/bootstrap.sh index e0bd8fd..6654b45 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,299 +1,9 @@ -#!/bin/bash +#!/usr/bin/bash -########################################################### -# -# Marc's environment bootstrap script. -# -# Table of contents -# -# 1. Helpers and utilities -# These functions are used by individual bootstrap -# steps and are reused all over. -# 2. Steps -# Functions implementing a specific action in the -# bootstrap process. -# 3. Bootstrap sequence and callsite -# Where the magic happens. -# This is where the ordered sequence of steps -# is defined and run. -# -########################################################### +VENV=env.venv -CURRENT_TIME=$(date +%T) +python -m venv "$VENV" -########################################################### -# -# 1. Helpers and utilities -# -########################################################### +. "$VENV/bin/activate" -STEP_COUNT=0 -SUBSTEP_COUNT=0 - -# Pretty-print step headers. -pre_step() { - STEP_COUNT=$((STEP_COUNT + 1)) - SUBSTEP_COUNT=0 - echo -e "\e[1m[$STEP_COUNT/$TOTAL_STEPS] <====== $1 =======>\e[0m" -} - -# Pretty-prints substep headers. -pre_substep() { - SUBSTEP_COUNT=$((SUBSTEP_COUNT + 1)) - echo -e "\e[1m[$STEP_COUNT.$SUBSTEP_COUNT] <====== $1 =======>\e[0m" -} - -# Copies a file from $1 to $2. -# -# If $2 exists, checks if $1 == $2 and asks what to do. -# From there, the user providing "O" will overwrite (proceed with copy), -# "S" will skip the file and leave the destination as-is, and anyting else -# will skip the copy altogether (i.e. "S"). -copy_file() { - source_path=$1 - dest_path=$2 - - if [ ! -e "$dest_path" ]; then - cp "$source_path" "$dest_path" - echo "Copied $source_path > $dest_path" - return 0 - fi - - difference=$(diff "$dest_path" "$source_path") - - if [ -z "$difference" ]; then - echo "$dest_path already exists, but is the same as $source_path - skipping." - return 0 - fi - - echo "Differences detected between $source_path and $dest_path:" - diff --color "$dest_path" "$source_path" - read -p "$dest_path already exists. What should be done? [O:overwrite,S:skip,B:save backup and copy] " -r action - - if [[ $action == "O" ]]; then - cp "$source_path" "$dest_path" - echo "Overwrote $source_path > $dest_path" - return 0 - fi - - if [[ $action == "S" ]]; then - echo "Skipped and left $dest_path as is." - return 0 - fi - - if [[ $action == "B" ]]; then - cp "$dest_path" "$dest_path".old - echo "Backed up $dest_path up to $dest_path.old" - cp "$source_path" "$dest_path" - echo "Overwrote $source_path > $dest_path" - return 0 - fi - - echo "Unrecognized action, doing nothing." -} - -########################################################## -# -# 2. Steps -# -########################################################## - -# Ensures that all existing Apt packages are up-to-date. -ensure_apt_up_to_date() { - pre_step "Ensuring apt packages are up-to-date" - - if [ -z "$(apt --version 2> /dev/null)" ]; then - echo -e "\e[33mApt not installed, skipping updates.\e[0m" - else - echo -e "\e[1mEnsuring system packages are up-to-date...\e[0m" - - sudo apt update && sudo apt upgrade -y --autoremove - - echo -e "\e[1;32mSystem packages up-to-date.\e[0m" - fi -} - -# Installs packages as specified in apt.txt -ensure_apt_dependencies() { - pre_step "Installing extra packages" - - if [ -z "$(apt --version 2> /dev/null)" ]; then - echo -e "\e[33mApt not installed, skipping packages.\e[0m" - else - echo -e "\e[1mInstalling packages...\e[0m" - - sudo apt install "$(cat ./files/apt.txt)" -y --autoremove - - echo -e "\e[1;32mSystem packages installed.\e[0m" - fi -} - -# Installs the rustup toolchain if not installed. -# If installed, the toolchain is updated. -install_rust() { - pre_step "Install and configure Rust toolchain" - - if [ -z "$(rustup --version 2> /dev/null)" ]; then - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - else - rustup update - fi -} - -install_omz() { - pre_step "Installs Oh My ZSH" - curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh | bash -} - -# Installs the Starship prompt and adds an initialization -# command to the shell configuration file. -# -# Requires: install_rust -install_and_configure_starship() { - pre_step "Install and configure Starship" - - pre_substep "Install Starship via cargo" - cargo install starship --locked - - pre_substep "Add initialization command to shell configuration" - if [ -z "$(rg "starship init zsh" ~/.zshrc)" ]; then - printf "#Initialize Starship prompt\neval \"\$(starship init zsh)\"" >> ~/.zshrc - fi - - pre_substep "Copy configuration in configuration directory" - - copy_file ./files/starship.toml ~/.config/starship.toml -} - -install_nvm() { - pre_step "Installing nvm to manage node version" - - pre_substep "Installing NVM from remote" - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash - - pre_substep "Ensuring that NVM can be run right away" - # Ensuring that nvm is usable right away without restarting the shell - export NVM_DIR="$HOME/.nvm" - # shellcheck disable=SC1091 - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" - # shellcheck disable=SC1091 - [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" -} - -install_gh_plugins() { - pre_step "Installing gh cli plugins" - - gh extension install nektos/gh-act - - gh extension install dlvhdr/gh-dash - copy_file ./files/gh-dash_config."$ENV_PROFILE".yml ~/.config/gh-dash/config.yml - - gh extension upgrade --all -} - -install_pyenv() { - pre_step "Install and configure pyenv" - - curl https://pyenv.run | bash -} - -# Injects a managed block in the shell configuration. -inject_shell_configuration() { - pre_step "Injecting managed block in shell configuration" - - BLOCK_DELIMITER_PATTERN="mcataford/env" - WORKING_PATH=$(git rev-parse --show-toplevel) - SHELL_CONFIG_PATH="$HOME/.zshrc" - - echo "Setting up shell configuration extras..." - if ! grep -q "$BLOCK_DELIMITER_PATTERN" < "$SHELL_CONFIG_PATH"; then - TEMPORARY_PATH=/tmp/init_vim_$CURRENT_TIME - copy_file "$SHELL_CONFIG_PATH" "$TEMPORARY_PATH" - printf "# %s:start\nsource %s/files/shell_extras\n# %s:end" \ - "$BLOCK_DELIMITER_PATTERN" "$WORKING_PATH" "$BLOCK_DELIMITER_PATTERN" \ - >> "$TEMPORARY_PATH" - copy_file "$TEMPORARY_PATH" "$SHELL_CONFIG_PATH" - echo "✅ Added managed block to $SHELL_CONFIG_PATH" - rm "$TEMPORARY_PATH" - else - echo "No changes to apply!" - fi -} - -# Injects a managed block in the vim configuration -inject_vim_configuration() { - pre_step "Inject managed block in vim configuration" - - EDITOR_CONFIG=$HOME/.config/nvim - EDITOR_CONFIG_FILE=$EDITOR_CONFIG/init.vim - WORKING_PATH=$(git rev-parse --show-toplevel) - - if [[ -f $EDITOR_CONFIG_FILE ]]; then - echo "Setting up NVIM configuration extras..." - if ! grep -q "$BLOCK_DELIMITER_PATTERN" < "$EDITOR_CONFIG_FILE"; then - TEMPORARY_PATH=/tmp/init_vim_$CURRENT_TIME - printf "\" %s:start\nsource %s/files/extras.vim\n\" %s:end\n\n" \ - "$BLOCK_DELIMITER_PATTERN" "$WORKING_PATH" "$BLOCK_DELIMITER_PATTERN" \ - >> "$TEMPORARY_PATH" - cat "$EDITOR_CONFIG_FILE" >> "$TEMPORARY_PATH" - - copy_file "$EDITOR_CONFIG_FILE" "$EDITOR_CONFIG_FILE".old - copy_file "$TEMPORARY_PATH" "$EDITOR_CONFIG_FILE" - echo "✅ Added managed block to $EDITOR_CONFIG_FILE" - rm "$TEMPORARY_PATH" - else - echo "No changes to apply!" - fi - fi - - echo "Setting up git configuration..." - source "$WORKING_PATH"/files/git_config - echo "✅ Set up git configuration, see $WORKING_PATH/files/git_config for details!" -} - -########################################################### -# -# 3. Bootstrap sequence -# -########################################################### - -bootstrap_home() { - TOTAL_STEPS=10 - - # System updates - ensure_apt_up_to_date - ensure_apt_dependencies - - # Development tooling and SDKs - install_rust - install_pyenv - install_nvm - install_gh_plugins - - # Shell & prompt setup - install_omz - install_and_configure_starship - - inject_shell_configuration - inject_vim_configuration -} - -bootstrap_work() { - TOTAL_STEPS=4 - - install_gh_plugins - - install_omz - install_and_configure_starship - - inject_shell_configuration - inject_vim_configuration -} - -if [[ $ENV_PROFILE == "home" ]]; then - bootstrap_home -elif [[ $ENV_PROFILE == "work" ]]; then - bootstrap_work -else - echo "Unknown \$ENV_PROFILE. :shrug:" -fi +pip install -U pip ansible~=9.5 diff --git a/files/apt.txt b/files/apt.txt deleted file mode 100644 index 9f679bf..0000000 --- a/files/apt.txt +++ /dev/null @@ -1,6 +0,0 @@ -zsh -tmux -postgresql -postgresql-contrib -gh -shellcheck diff --git a/files/gh-dash_config.home.yml b/files/gh-dash.yml similarity index 100% rename from files/gh-dash_config.home.yml rename to files/gh-dash.yml diff --git a/files/git_config b/files/git_config deleted file mode 100644 index 156283c..0000000 --- a/files/git_config +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# Always sign commits. -git config --global commit.gpgsign true - -# Prune stale refs on fetch. -git config --global fetch.prune true diff --git a/files/shell_extras b/files/shell_extras index 590c862..9007a5c 100644 --- a/files/shell_extras +++ b/files/shell_extras @@ -1,5 +1,3 @@ -#!/usr/bin/bash - ################ # ALIASES # ################ diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..7b7c5af --- /dev/null +++ b/playbook.yml @@ -0,0 +1,59 @@ +--- +- name: Local environment + hosts: localhost + + tasks: + - name: Ensure system up-to-date + become: yes + apt: + update-cache: yes + upgrade: yes + - name: Install terminal & shell + apt: + pkg: + - zsh + - tmux + - name: Install CLI tooling + apt: + pkg: + - shellcheck + - gh + - name: Install gh CLI extensions (gh-dash) + command: gh extension install dlvhdr/gh-dash + - name: Install DB-related tooling + apt: + pkg: + - postgresql + - postgresql-contrib + - name: Add zsh managed block + blockinfile: + path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.zshrc" + marker: "# Marc's env managed block - {mark}" + block: "{{ lookup('ansible.builtin.file', 'files/shell_extras') }}" + - name: Add nvim managed block + blockinfile: + path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/nvim/init.vim" + marker: "\" Marc's env managed block - {mark}" + block: "{{ lookup('ansible.builtin.file', 'files/extras.vim') }}" + - name: Add starship managed block + blockinfile: + path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/starship.toml" + marker: "# Marc's env managed block - {mark}" + block: "{{ lookup('ansible.builtin.file', 'files/starship.toml') }}" + - name: Ensure gh-dash config directory exists + file: + path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/gh-dash" + state: directory + - name: Ensure gh-dash config exists + file: + path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/gh-dash/config.yml" + state: touch + - name: Add gh-dash configuration managed block + blockinfile: + path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/gh-dash/config.yml" + marker: "# Marc's env managed block - {mark}" + block: "{{ lookup('ansible.builtin.file', 'files/gh-dash.yml') }}" + - name: Configure git + shell: | + git config --global commit.gpgsign true + git config --global fetch.prune true diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..b4e93fa --- /dev/null +++ b/setup.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +ansible-playbook playbook.yml -v --ask-become-pass