pour all my fish config into this repo
- ID
25d9f81- date
2023-11-26 02:16:00+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
5f337c6- message
pour all my fish config into this repo- changed files
10 files, 389 additions, 38 deletions
Changed files
README.md (2876) → README.md (1493)
diff --git a/README.md b/README.md
index 0f87c44..030a51f 100644
--- a/README.md
+++ b/README.md
@@ -1,41 +1,32 @@
# scripts
-This is a collection of useful scripts and tools I keep in my [PATH].
+This is a collection of various scripts and tools I find useful.
I use this Git repository to sync them across multiple computers.
-I use scripts over shell config for a couple of reasons:
+## Installation
-- Individual scripts are more portable.
- I can send you a script and you can start using it immediately, even if you use a different shell.
+To set up this repo on a new computer, I run the following commands in a Fish shell:
-- I can use different languages.
- I'm not restricted to whatever my shell uses.
+1. Clone the repository:
-- It makes my shell start faster.
- I use [fish], and I've noticed that if I write large fish config files, there's a noticeable delay between starting a shell and getting my first prompt.
- Using scripts means I have smaller config files, and I get a first prompt faster.
+ ```console
+ $ git clone git@github.com:alexwlchan/scripts.git ~/repos/scripts
+ $ cd ~/repos/scripts
+ ```
-[PATH]: https://en.wikipedia.org/wiki/PATH_(variable)
-[fish]: https://fishshell.com/
+2. Create a Python virtualenv and install dependencies:
-## Usage
+ ```console
+ $ python3 -m venv .venv
+ $ source .venv/bin/activate.fish
+ $ pip install -r requirements.txt
+ ```
+
+3. Install my Fish config, so Fish knows where to find all these scripts:
-Individual scripts have header comments explaining what they do.
-Download them and add them to your PATH.
-
-You can also clone this entire repo, then add it to your PATH, if you want to use all the scripts.
-
-For example, I have the following code in [my fishconfig](https://github.com/alexwlchan/fishconfig/blob/main/config.fish#L5-L22):
-
-```shell
-set --global --export PATH $PATH \
- ~/repos/scripts \
- ~/repos/scripts/aws \
- ~/repos/scripts/git \
- ~/repos/scripts/installers \
- ~/repos/scripts/macos \
- ~/repos/scripts/terraform
-```
+ ```console
+ $ ln -s ~/repos/scripts/config.fish ~/.config/fish/config.fish
+ ```
## Organisation
@@ -50,13 +41,3 @@ The script in this repo are pretty short – typically 50 lines or less (includi
They're mostly stuff that I can write all in one go.
If a script gets sufficiently large and complicated that it might benefit from its own documentation or change history, it "graduates" into a separate repo.
-
-## Contributors
-
-My scripts include code written by other people, and in those cases I've [attributed the commit that added the code/script to multiple authors][trailer].
-This is why several people who aren't me appear in then list of contributors, even though none of them have ever directly committed code.
-
-You can see all the (documented) instances of other people's code by searching for the [Co-authored-by line][search] in my commits.
-
-[trailer]: https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors#:~:text=In%20the%20text%20box%20below,Commit%20changes%20or%20Propose%20changes.
-[search]: https://github.com/search?q=repo%3Aalexwlchan%2Fscripts%20co-authored-by&type=commits
config.fish (0) → config.fish (2207)
diff --git a/config.fish b/config.fish
new file mode 100644
index 0000000..09bdc83
--- /dev/null
+++ b/config.fish
@@ -0,0 +1,64 @@
+# Don't show a greeting on startup. In particular, don't show
+# this message:
+#
+# Welcome to fish, the friendly interactive shell
+# Type help for instructions on how to use fish
+#
+set -g -x fish_greeting ''
+
+# This tells fish to find functions in my "fish_functions" directory.
+#
+# See https://fishshell.com/docs/current/language.html#autoloading-functions
+set -x fish_function_path ~/repos/scripts/fish_functions $fish_function_path
+
+
+# Load macOS-specific utilities
+if [ (uname -s) = "Darwin" ]
+ # Provide a convenient alias for the front URL in both browsers
+ alias furl="safari url"
+ alias gurl="osascript -e 'tell application \"Google Chrome\" to tell front window to get URL of tab (active tab index)'"
+
+ # Get the URL of the frontmost GitHub page and clone it
+ function gh-clone
+ _ensure_ssh_key_loaded
+ github-clone (furl)
+ end
+
+end
+
+
+# Taken from https://gist.github.com/tommyip/cf9099fa6053e30247e5d0318de2fb9e
+#
+# This will automatically enable/disable my virtualenvs when I enter/leave directories.
+#
+# Based on https://gist.github.com/bastibe/c0950e463ffdfdfada7adf149ae77c6f
+# Changes:
+# * Instead of overriding cd, we detect directory change. This allows the script to work
+# for other means of cd, such as z.
+# * Update syntax to work with new versions of fish.
+# * Handle virtualenvs that are not located in the root of a git directory.
+
+function __auto_source_venv --on-variable PWD --description "Activate/Deactivate virtualenv on directory change"
+ status --is-command-substitution; and return
+
+ # Check if we are inside a git directory
+ if git rev-parse --show-toplevel &>/dev/null
+ set gitdir (realpath (git rev-parse --show-toplevel))
+ set cwd (pwd)
+ # While we are still inside the git directory, find the closest
+ # virtualenv starting from the current directory.
+ while string match "$gitdir*" "$cwd" &>/dev/null
+ if test -e "$cwd/.venv/bin/activate.fish"
+ source "$cwd/.venv/bin/activate.fish" &>/dev/null
+ return
+ else
+ set cwd (path dirname "$cwd")
+ end
+ end
+ end
+
+ # If virtualenv activated but we are not in a git directory, deactivate.
+ if test -n "$VIRTUAL_ENV"
+ deactivate
+ end
+end
fish_functions/fish_prompt.fish (0) → fish_functions/fish_prompt.fish (4380)
diff --git a/fish_functions/fish_prompt.fish b/fish_functions/fish_prompt.fish
new file mode 100644
index 0000000..e8dde1b
--- /dev/null
+++ b/fish_functions/fish_prompt.fish
@@ -0,0 +1,157 @@
+###############################################################################
+# My fish prompt
+#
+# This has been inspired by various examples from other people, not all
+# of whom I kept notes of.
+###############################################################################
+
+
+function print_current_directory
+ set_color green
+ printf (echo -n (prompt_pwd))
+ set_color normal
+end
+
+
+# Print information about the current Git branch, if I'm in a Git repo.
+#
+# At one point I had similar functions for getting SVN and Mercurial information,
+# but at time of writing (Apr 2022), it's been 5+ years since I used a non-Git VCS.
+# It's not worth maintaining those alternatives or running them against every
+# shell prompt.
+function print_git_information
+ which git 2>&1 >/dev/null
+ if [ $status = "0" ]
+ set branch (git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
+ if [ -n "$branch" ]
+ set_color normal
+ printf " on git:"
+
+ if test (basename "$branch") = "main"
+ set_color cyan
+ else
+ set_color purple
+ end
+
+ printf "$branch"
+
+ # Print an asterisk to indicate uncommitted changes, if there are any
+ if ! git diff-index --quiet HEAD --
+ printf "*"
+ end
+
+ set_color normal
+ end
+ end
+end
+
+
+# Print information about the current virtualenv, if one is enabled.
+#
+# The VIRTUAL_ENV_DISABLE_PROMPT command disables the auto-prepending of the
+# venv into my prompt by the virtualenv itself; see
+# https://stackoverflow.com/a/63029769/1558022
+set -x VIRTUAL_ENV_DISABLE_PROMPT 1
+
+function print_venv_information
+ if [ -n "$VIRTUAL_ENV" ]
+ set_color normal
+ printf " using "
+
+ if test (basename "$VIRTUAL_ENV") = ".venv"
+ set_color cyan
+ else
+ set_color purple
+ end
+
+ printf (basename "$VIRTUAL_ENV")
+ set_color normal
+ end
+end
+
+
+# If I'm running over SSH, prepend the name of the remote host to
+# the context line.
+function print_ssh_information
+ if set -q SSH_CLIENT
+ printf "("
+ set_color purple
+ printf (echo -n (hostname))
+ set_color normal
+ printf ") "
+ end
+end
+
+
+# Allow me to prevent certain dangerous commands from ever
+# appearing in autocomplete.
+#
+# See https://alexwlchan.net/2023/forgetful-fish/
+# See https://github.com/fish-shell/fish-shell/issues/10066
+function forget_dangerous_history_commands
+ set last_typed_command (history --max 1)
+
+ if [ "$last_typed_command" = "git push origin (gcb) --force" ]
+ history delete --exact --case-sensitive "$last_typed_command"
+ history save
+ end
+end
+
+
+function fish_prompt --description 'Write out the prompt'
+ # forget_dangerous_history_commands
+
+ # Put a newline between new prompts for cleanliness, but not on the first run.
+ #
+ # This means the first prompt of a new session is right at the top of
+ # the terminal window, not with a newline above it.
+ #
+ # If we're in an SSH session, we always insert a newline, even on the first
+ # command -- to separate from the client session. I avoid getting the
+ # 'Last login' message with `touch ~/.hushlogin`
+ if set -q SSH_CLIENT
+ echo ''
+ else
+ if test \( -f "/tmp/$TERM_SESSION_ID" -o -f "/tmp/$XDG_SESSION_ID" \)
+ echo ''
+ end
+
+ touch "/tmp/$TERM_SESSION_ID" 2>/dev/null
+ touch "/tmp/$XDG_SESSION_ID" 2>/dev/null
+ end
+
+ # Print some context about where I'm running this command.
+ #
+ # If I'm in my home directory, the context isn't very interesting (it's where
+ # new shells open, and it's not in Git), so skip the context line to reduce
+ # visual noise.
+ if [ (prompt_pwd) = "~" ]
+ if set -q SSH_CLIENT
+ print_ssh_information
+ echo ''
+ end
+ echo '$ '
+ return
+ end
+
+ print_ssh_information
+ print_current_directory
+ print_git_information
+ print_venv_information
+
+ # Print the shell prompt.
+ #
+ # I have a different prompt for when I'm running as root; admittedly this
+ # is extremely rare if I'm also using fish, but if I am I want a visual cue
+ # that this terminal is unusual.
+ #
+ # I print the prompt on a separate line to the context information so it's
+ # always in the same place: as I'm typing commands, I get the full width of
+ # the terminal to use, rather than a variable amount based on the context line.
+ set_color normal
+ if [ "$USER" = "root" ]
+ echo '' & echo '# '
+ else
+ echo '' & echo '$ '
+ end
+end
fish_functions/forget_last_command.fish (0) → fish_functions/forget_last_command.fish (521)
diff --git a/fish_functions/forget_last_command.fish b/fish_functions/forget_last_command.fish
new file mode 100644
index 0000000..3f2e225
--- /dev/null
+++ b/fish_functions/forget_last_command.fish
@@ -0,0 +1,13 @@
+# Removes the last-typed command from my fish history.
+#
+# This means that if I mistype a command and it starts appearing in
+# my suggested commands, I can type it one more time then purge it from
+# my history, to prevent it being suggested again.
+#
+# See https://alexwlchan.net/2023/forgetful-fish/
+# See https://github.com/fish-shell/fish-shell/issues/10066
+function forget_last_command
+ set last_typed_command (history --max 1)
+ history delete --exact --case-sensitive "$last_typed_command"
+ history save
+end
fish_functions/gh-add-remote.fish (0) → fish_functions/gh-add-remote.fish (333)
diff --git a/fish_functions/gh-add-remote.fish b/fish_functions/gh-add-remote.fish
new file mode 100644
index 0000000..298da80
--- /dev/null
+++ b/fish_functions/gh-add-remote.fish
@@ -0,0 +1,7 @@
+# Within a GitHub repository, create a remote named 'alex' for a GitHub fork
+# of the same name. Useful if I cloned before forking.
+function gh-add-remote
+ set origin_url (git remote get-url origin)
+ set repo_name (string split "/" "$origin_url" | tail -n 1)
+ git remote add alex "git@github.com:alexwlchan/$repo_name"
+end
fish_functions/github-add-pr-branch.fish (0) → fish_functions/github-add-pr-branch.fish (1445)
diff --git a/fish_functions/github-add-pr-branch.fish b/fish_functions/github-add-pr-branch.fish
new file mode 100644
index 0000000..e2754e3
--- /dev/null
+++ b/fish_functions/github-add-pr-branch.fish
@@ -0,0 +1,41 @@
+# Given a GitHub pull request, create the repo and make sure the remote
+# of the PR owner is added as a remote.
+#
+# $1 = URL of the pull request
+#
+# Sometimes when I'm looking at a pull request, it's useful to get the
+# branch locally and test/review/squash it as appropriate. This makes
+# it easier to do so!
+#
+# Typically not called directly, but detected by 'github-open' and
+# switched to if looking at a pull request URL.
+function github-add-pr-branch
+ # First ensure we have a local clone of the repo
+ set url "$argv[1]"
+ github-clone (string split "pull/" "$url" | head -n 1)
+ if [ $status != 0 ]
+ return 1
+ end
+
+ # Get the identifiers for the repository. A pull request URL is
+ # of the form
+ #
+ # https://github.com/:owner/:repo/pull/:number#discussion_:comment
+ #
+ set components (string split "/" "$url")
+
+ if [ "$components[6]" != "pull" ]
+ echo "$url is not a GitHub pull request"
+ return 1
+ end
+
+ set owner $components[4]
+ set repo $components[5]
+ set number (echo $components[7] | tr '#' ' ' | awk '{print $1}')
+
+ set api_url "https://api.github.com/repos/$owner/$repo/pulls/$number"
+ set api_resp (curl -s -H "Accept: application/vnd.github.v3+json" "$api_url")
+ set pr_branch (echo $api_resp | jq '.head.repo.full_name' | tr '"' ' ' | awk '{print $1}')
+
+ git checkout (echo $api_resp | jq '.head.ref' | tr '"' ' ' | awk '{print $1}')
+end
\ No newline at end of file
fish_functions/github-clone.fish (0) → fish_functions/github-clone.fish (1983)
diff --git a/fish_functions/github-clone.fish b/fish_functions/github-clone.fish
new file mode 100644
index 0000000..22d51ac
--- /dev/null
+++ b/fish_functions/github-clone.fish
@@ -0,0 +1,64 @@
+# Clone a GitHub repo given its URL.
+#
+# $1 = URL of the GitHub page
+#
+# Because switching to the repo homepage, clicking, copying the clone URL,
+# typing 'git clone', pasting, are all more effort than I care to do manually.
+function github-clone
+ set url "$argv[1]"
+
+ # Get the identifiers for the repository
+ set components (string split "/" "$url")
+
+ if [ "$components[3]" != "github.com" ]
+ echo "$url is not a GitHub repo"
+ return 1
+ end
+
+ set owner $components[4]
+ set repo $components[5]
+
+ if [ (count $components) -gt 5 ]
+ # Detect if this is a pull request, and divert
+ if [ $components[6] = "pull" ]
+ github-add-pr-branch "$url"
+ return $status
+ end
+ end
+
+ set repo_url "git@github.com:$owner/$repo.git"
+
+ mkdir -p ~/repos; cd ~/repos
+
+ if [ -d $repo ]
+ # If the repo already exists, check we have the selected fork
+ # as a remote.
+ cd $repo
+ git remote -v | grep "$owner" >/dev/null 2>&1
+ if [ $status != 0 ]
+ echo "git remote add $owner $repo_url"
+ git remote add $owner $repo_url
+ end
+ set remote (git remote -v | grep "$owner" | awk '{print $1}')
+ git fetch
+ else
+ # Otherwise, clone a fresh copy of the repo
+ echo "git clone $repo_url"
+ git clone $repo_url
+ cd $repo
+
+ # If this looks like a Python repository, create a virtualenv
+ # in the root of the repo.
+ if [ -f "requirements.txt" ]
+ echo "Creating virtualenv..."
+ new_venv
+ end
+
+ # I auto-populate .git/info/exclude with a few common entries to
+ # save having to do it later. (I could use a global .gitignore,
+ # but this way it's managed programatically and all local to the
+ # repo, which I slightly prefer to a homefolder full of manually
+ # managed dotfiles.)
+ echo .DS_Store >> .git/info/exclude
+ end
+end
\ No newline at end of file
fish_functions/reload_fish_config.fish (0) → fish_functions/reload_fish_config.fish (63)
diff --git a/fish_functions/reload_fish_config.fish b/fish_functions/reload_fish_config.fish
new file mode 100644
index 0000000..e9e4b9d
--- /dev/null
+++ b/fish_functions/reload_fish_config.fish
@@ -0,0 +1,3 @@
+function reload_fish_config
+ . ~/.config/fish/config.fish
+end
fish_functions/tempdir.fish (0) → fish_functions/tempdir.fish (108)
diff --git a/fish_functions/tempdir.fish b/fish_functions/tempdir.fish
new file mode 100644
index 0000000..3067114
--- /dev/null
+++ b/fish_functions/tempdir.fish
@@ -0,0 +1,3 @@
+function tmpdir --description "Quickly create and switch into a temporary directory"
+ cd (mktemp -d)
+end
fish_functions/venv.fish (0) → fish_functions/venv.fish (485)
diff --git a/fish_functions/venv.fish b/fish_functions/venv.fish
new file mode 100644
index 0000000..9532010
--- /dev/null
+++ b/fish_functions/venv.fish
@@ -0,0 +1,18 @@
+# Create and activate a new virtualenv.
+#
+# This is to prevent me from making a very common mistake, which is
+# creating the venv and then immediately running "pip install" without
+# activating it first.
+#
+# I upgrade pip because otherwise I get warnings about it being
+# out-of-date, and that's annoying.
+function new_venv
+ python3 -m venv .venv
+ source .venv/bin/activate.fish
+
+ python3 -m pip install --upgrade pip
+
+ if [ -f .git ]
+ echo .venv >> .git/info/exclude
+ end
+end