Why I’m Always Trying to Improve My Terminal Workflow

I have been using terminal/tmux/bash for several years now, constantly striving to enhance my workflow. As a freelancer, I often juggle multiple heterogeneous projects simultaneously, which motivates me to streamline my approach. I have automated my laptop setup entirely using Ansible, assigning a unique “role” to each project.

Within each role, I had a bashrc file that I used to source globally.

However, a major challenge I face is the inability to reuse the same alias across different projects. Consequently, I resorted to prefixing everything. For instance, when working on Camino, I had to create an alias called “camino_pr” for generating pull requests. This approach has its drawbacks, such as the alias “camino_pr” being available everywhere but only usable in certain directories.

Therefore, I explored various solutions to load specific .bashrc files based on my current working directory. Here are some existing options I found:

  • Direnv: It allows loading environment variables only.
  • Ondir: This tool relies on a single ~/.ondirrc file, making it difficult to manage with multiple heterogeneous projects.
  • ZSH Autoenv: Unfortunately, it is designed exclusively for zsh users.
  • Smartcd: This tool aligns with my requirements, but I prefer a more bash-based approach.

Hacking CD to Achieve My Goal

My requirement is simple: I want to automatically source one or more files when I enter a directory.

One potential solution is to hook the cd command, so that every time I use cd it executes a bash function.

For example, consider the following bash function:

function cd() {
  builtin cd "$@"
  echo "Moving to $(pwd)"
}

This function will print the new path every time I switch to a different directory.

In the end, what I aim for is to source any .my_bash_file encountered along my current path.

After some experimentation, I arrived at the following implementation:


function cd() {
  builtin cd "$@"
  local x=$(pwd)
  local bash_files_to_source=()

  while [ "$x" != "/" ]; do
    if [ -f "${x}/.my_bash_file" ]; then
        bash_files_to_source+=("${x}/.my_bash_file")
    fi
    x=$(dirname "$x")
  done

  local current_index=$(( ${#bash_files_to_source[@]} - 1 ))
  while [[ current_index -gt -1 ]]; do
    local bash_file_to_source="${bash_files_to_source[$current_index]}"
    echo "Sourcing ${bash_file_to_source}"
    source "${bash_file_to_source}"
    ((current_index--))
  done
}

I added this code to my .bashrc, along with a cd . command at the end to activate it when I open a new tmux pane, for example.

Enhanced Usage

This approach provides significant flexibility. Now, I have a consistent pr command available in every project I work on. Sometimes, it is aliased to gh pr create, other times it might be a custom Python command or any other tool used by the team I collaborate with.

Moreover, I have even automated the retrieval of secrets from my keepassxc instance when entering specific folders. As a result, most of my secrets are no longer stored physically on my machine. (I will likely write another blog post to discuss secret service integration in detail.)

TODO

  • Unsource stuff when I move out
  • Secure what I can source or not?