Compare commits

..

No commits in common. "main" and "feat/better-handling-of-copied-static-files" have entirely different histories.

23 changed files with 361 additions and 283 deletions

View file

@ -1,9 +0,0 @@
on:
push:
jobs:
sast:
runs-on: runner-latest
steps:
- uses: actions/checkout@v4
- run: pipx run pre-commit run -a

1
.gitignore vendored
View file

@ -1 +0,0 @@
*.venv

View file

@ -1,6 +0,0 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml

View file

@ -1 +0,0 @@
3.12

View file

@ -1,14 +0,0 @@
FROM debian:bookworm
COPY . .
RUN apt update && apt upgrade -y --autoremove
RUN apt install -y\
pipx \
make \
sudo
ENV PATH="$PATH:/root/.local/bin"
CMD make configure-env PLAY=./home.yml

View file

@ -1,9 +1,43 @@
# 🥫Environment-in-a-can
Environment tweaks for (my) everyday happiness.
Environment tweaks for everyday happiness.
Do the thing, it will do the needful: `make configure-env PLAY=<playbook-path>`.
## Setup
## Development
```bash
git clone git@github.com:mcataford/env.git <path-of-your-choosing> | . ./bootstrap.sh
```
Repository prerequisites can be installed and validated via `make bootstrap` and containerized playbook tests can be run via `make test`.
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.
## 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
starship.toml # Starship prompt configuration
```
Adding code to `shell_extras` will add code that gets executed on shell-start.
### Adding steps
Each step is defined as a function in `steps.sh` and should start with a call to `pre_step` to ensure that a header gets
echo'ed out. Additionally, the function should be called from `bootstrap` in `bootstrap.sh` and the `TOTAL_STEPS` should
be updated to reflect the number of steps.
## Contributing
I'm not currently looking for contributions since this is mainly about standardizing my own setup across machines.

275
bootstrap.sh Executable file
View file

@ -0,0 +1,275 @@
#!/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.
#
###########################################################
###########################################################
#
# 1. Helpers and utilities
#
###########################################################
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 $source_path $dest_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 $source_path $dest_path
read -p "$dest_path already exists. What should be done? [O:overwrite,S:skip,B:save backup and copy] " 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"
STARSHIP_INIT_COMMAND='eval "$(starship init zsh)"'
if [ -z "$(rg "starship init zsh" ~/.zshrc)" ]; then
echo "#Initialize Starship prompt\n$STARSHIP_INIT_COMMAND" >> ~/.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"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -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.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 [[ -z $(cat $SHELL_CONFIG_PATH | grep $BLOCK_DELIMITER_PATTERN) ]]; then
echo "# $BLOCK_DELIMITER_PATTERN\:start
source $WORKING_PATH/files/shell_extras
# $BLOCK_DELIMITER_PATTERN\:end" >> $SHELL_CONFIG_PATH
echo "✅ Added managed block to $SHELL_CONFIG_PATH"
else
echo "No changes to apply!"
fi
EDITOR_CONFIG=$HOME/.config/nvim
EDITOR_CONFIG_FILE=$EDITOR_CONFIG/init.vim
}
# Injects a managed block in the vim configuration
inject_vim_configuration() {
pre_step "Inject managed block in vim configuration"
WORKING_PATH=$(git rev-parse --show-toplevel)
if [[ -f $EDITOR_CONFIG_FILE ]]; then
echo "Setting up NVIM configuration extras..."
if [[ -z $(cat $EDITOR_CONFIG_FILE | grep $BLOCK_DELIMITER_PATTERN) ]]; then
echo "\" $BLOCK_DELIMITER_PATTERN\:start
source $WORKING_PATH/files/extras.vim
\" $BLOCK_DELIMITER_PATTERN\:end\n\n" >> $EDITOR_CONFIG_FILE.new
cat $EDITOR_CONFIG_FILE >> $EDITOR_CONFIG_FILE.new
mv $EDITOR_CONFIG_FILE $EDITOR_CONFIG_FILE.old
mv $EDITOR_CONFIG_FILE.new $EDITOR_CONFIG_FILE
echo "✅ Added managed block to $EDITOR_CONFIG_FILE"
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
#
###########################################################
TOTAL_STEPS=10
# Refer to steps.sh for step definition.
bootstrap() {
# 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
return

5
files/apt.txt Normal file
View file

@ -0,0 +1,5 @@
zsh
tmux
postgresql
postgresql-contrib
gh

View file

@ -22,7 +22,7 @@ set termguicolors
call plug#begin('~/.vim/plugged')
" Style
Plug 'bluz71/vim-nightfly-colors', { 'as': 'nightfly', 'branch': 'legacy' }
Plug 'joshdick/onedark.vim'
" Language support
Plug 'maxmellon/vim-jsx-pretty'
@ -44,6 +44,7 @@ Plug 'nvim-telescope/telescope-symbols.nvim'
Plug 'mcataford/ale'
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}
call plug#end()
""""""""""""""""
@ -55,6 +56,6 @@ let mapleader=" "
nmap <leader>f <cmd>Telescope live_grep<CR>
nmap <leader>F <cmd>Telescope grep_string<CR>
colorscheme nightfly
colorscheme onedark

View file

@ -1,18 +1,17 @@
---
prSections:
- title: WIP
filters: is:open author:@me -org:tophatmonocle
- title: Needs My Review
filters: is:open review-requested:@me -org:tophatmonocle
- title: Involved
filters: is:open involves:@me -author:@me -org:tophatmonocle
- title: WIP
filters: is:open author:@me -org:tophatmonocle
- title: Needs My Review
filters: is:open review-requested:@me -org:tophatmonocle
- title: Involved
filters: is:open involves:@me -author:@me -org:tophatmonocle
issuesSections:
- title: My Issues
filters: is:open author:@me
- title: Assigned
filters: is:open assignee:@me
- title: Involved
filters: is:open involves:@me -author:@me
- title: My Issues
filters: is:open author:@me
- title: Assigned
filters: is:open assignee:@me
- title: Involved
filters: is:open involves:@me -author:@me
defaults:
preview:
open: true

7
files/git_config Normal file
View file

@ -0,0 +1,7 @@
#!/bin/bash
# Always sign commits.
git config --global commit.gpgsign true
# Prune stale refs on fetch.
git config --global fetch.prune true

View file

@ -1,3 +1,5 @@
#!/usr/bin/bash
################
# ALIASES #
################
@ -49,23 +51,3 @@ function which_alias {
# Ensures that passphrases can be entered when signing
# git commits.
export GPG_TTY=$(tty)
############
# CONFIG #
############
# Basic ZSH things.
export ZSH="/home/marc/.oh-my-zsh"
ZSH_THEME="lambda"
plugins=(git)
# OMZ configuration.
source $ZSH/oh-my-zsh.sh
#Initialize Starship prompt
eval "$(starship init zsh)"
unsetopt autocd
# Local binaries.
export PATH=$PATH:$HOME/.local/bin

View file

@ -1,59 +1,45 @@
format = """
[](bold green) \
[](color_orange)\
$username$hostname\
[](#9A348E)\
$username\
$os\
[](bg:color_blue fg:color_orange)\
[](bg:#33658a fg:#9A348E)\
$time\
[](bg:color_green fg:color_blue)\
[](bg:#DA627D fg:#33658a)\
$directory\
[](fg:color_green bg:color_pink)\
[](fg:#DA627D bg:#3208e0)\
$git_branch\
$git_status\
[](fg:color_pink bg:color_red)\
[](fg:#4208e0 bg:#9A348E)\
$python\
[](fg:color_red)
[ ](bold green)
[](fg:#9A348E)
>
"""
# Disable the blank line at the start of the prompt
add_newline = false
palette = 'default'
[palettes.default]
color_blue = '#458588'
color_green = '#98971a'
color_orange = '#d65d0e'
color_pink = '#b16286'
color_red = '#cc241d'
add_newline = true
[python]
style = "bg:color_red"
style = "bg:#9A348E"
format = '[${symbol}${pyenv_prefix}(${version} )(\($virtualenv\) )]($style)'
# You can also replace your username with a neat symbol like  or disable this
# and use the os module below
[username]
show_always = true
style_user = "bg:color_orange"
style_root = "bg:color_orange"
format = '[$user]($style)'
style_user = "bg:#9A348E"
style_root = "bg:#9A348E"
format = '[$user ]($style)'
disabled = false
[hostname]
format = '[@$hostname ]($style)'
style = "bg:color_orange"
ssh_only = false
# An alternative to the username module which displays a symbol that
# represents the current operating system
[os]
style = "bg:color_red"
style = "bg:#9A348E"
disabled = true # Disabled by default
[directory]
style = "bg:color_green"
style = "bg:#DA627D"
format = "[ $path ]($style)"
truncation_length = 3
truncation_symbol = "…/"
@ -73,15 +59,15 @@ truncation_symbol = "…/"
[git_branch]
symbol = ""
style = "bg:color_pink"
style = "bg:#4208e0"
format = '[ $symbol $branch ]($style)'
[git_status]
style = "bg:color_pink"
style = "bg:#4208e0"
format = '[$all_status$ahead_behind ]($style)'
[time]
time_format = "%T"
style = "bg:color_blue"
style = "bg:#33658A"
format = '[ $time ]($style)'
disabled = false

View file

@ -1,10 +0,0 @@
---
- name: Local environment
hosts: localhost
roles:
- system-packages
- dev-toolchain
- shell-tweaks
- editor-tweaks
- tool-config
- productivity-tools

View file

@ -1,27 +0,0 @@
HELPTEXT="\
help: \n\
\tshows this helpful text.\n\
bootstrap: \n\
\tensures that repository-specific prerequisites are installed. \n\
test:\n\
\truns the playbook in a containerized environment for validation \n\
configure-env:\n\
\truns the playbook specified by PLAY on the host.\n\
"
.PHONY: help
help:
@echo $(HELPTEXT)
.PHONY: bootstrap
bootstrap:
pipx run pre-commit install
.PHONY: test
test:
podman build . -t env-ansible-test
podman run env-ansible-test
.PHONY: configure-env
configure-env:
pipx install "ansible-core"
ansible-playbook -v --ask-become-pass $(PLAY)

View file

@ -1,19 +0,0 @@
---
- name: Check if rustup is installed
shell: which rustup
register: which_rustup
ignore_errors: true
- name: Install rustup
shell: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
when: which_rustup.rc == 1
- name: Check if go is installed
shell: go version
register: check_go_version
ignore_errors: true
- name: Install go
become: true
shell: |
curl -o /tmp/go.tar.gz -L https://go.dev/dl/go{{go_version}}.linux-amd64.tar.gz &&
rm -rf /usr/local/go &&
sudo tar -C /usr/local -xzf /tmp/go.tar.gz
when: check_go_version.rc == 1 or check_go_version.stdout.find(go_version) == -1

View file

@ -1,2 +0,0 @@
---
go_version: 1.23.1

View file

@ -1,14 +0,0 @@
---
- name: Ensure general config dir exists
file:
path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/nvim"
state: directory
- name: Ensure .zshrc exists
file:
path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/nvim/init.vim"
state: touch
- 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') }}"

View file

@ -1,9 +0,0 @@
---
- name: Thunderbird
become: true
apt:
pkg: thunderbird
- name: TeXLive
become: true
apt:
pkg: texlive

View file

@ -1,50 +0,0 @@
---
- name: Ensure general config dir exists
file:
path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config"
state: directory
- name: Install OMZsh
shell: |
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
args:
creates: "{{ lookup('ansible.builtin.env', 'HOME') }}/.oh-my-zsh"
- name: Ensure .zshrc exists
file:
path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.zshrc"
state: touch
- 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: Install Starship
shell: $HOME/.cargo/bin/cargo install starship
- name: Ensure Starship config exists
file:
path: "{{ lookup('ansible.builtin.env', 'HOME') }}/.config/starship.toml"
state: touch
- 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') }}"
# TODO: Add gh cli login.
#- name: Check if gh-dash is installed
# shell: gh extension list
# register: gh_ext_list
#- name: Install gh CLI extensions (gh-dash)
# command: gh extension install dlvhdr/gh-dash
# when: gh_ext_list.stdout.find('gh dash') == -1
#- 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') }}"

View file

@ -1,42 +0,0 @@
---
- name: Ensure system up-to-date
become: true
apt:
update_cache: true
upgrade: "yes"
- name: Install Build Essentials
become: true
apt:
pkg:
- make
- build-essential
- cmake
- name: Install terminal & shell
become: true
apt:
pkg:
- zsh
- tmux
- name: Install CLI tooling
become: true
apt:
pkg:
- shellcheck
- gh
- curl
- git
- name: Install DB-related tooling
become: true
apt:
pkg:
- postgresql
- postgresql-contrib
- name: Install Podman
become: true
apt:
pkg:
- podman
- name: Install Ripgrep
become: true
apt:
deb: https://github.com/BurntSushi/ripgrep/releases/download/{{ripgrep_version}}/ripgrep_{{ripgrep_version}}-1_amd64.deb

View file

@ -1,2 +0,0 @@
---
ripgrep_version: 14.1.1

View file

@ -1,5 +0,0 @@
---
- name: Configure git
shell: |
git config --global commit.gpgsign true
git config --global fetch.prune true