Configuring your terminal on GitHub Codespaces

Created:

Topic: Development tools

Introduction

In this post, you’ll learn how to configure your terminal on GitHub Codespaces, ensuring that your remote environment feels as familiar as your local one. In order to get the most out of this post, it’s best that you already have access to GitHub Codespaces. As I explain in my blog post on Configuring Azure ML projects to run on Codespaces, your repo has access to Codespaces if it belongs to a GitHub organization that has Codespaces enabled, or if it sits in an individual account enrolled in Codespaces beta. If you’re doing Azure ML development, I highly recommend that you read the blog post I mentioned to get set up. Otherwise, I recommend getting set up by reading the GitHub Codespaces documentation.

We all have our terminal setup preferences, and it’s likely that your setup is very different from mine. This post will show you my personal setup, but the methods I use are universal, so you’ll be able to follow along while making choices that work for you. More specifically, I’ll show how you can configure your terminal using dotfiles, including how to setup the zsh Unix shell, the Oh My Zsh framework for managing zsh, the Spaceship custom theme, and the Powerline fonts. I’ll also show how you can configure custom colors for directory listings. You can follow along by looking at my simple dotfiles GitHub repo.

Even though my blog generally focuses on machine learning using Python, this post should be more broadly useful to anyone working on GitHub Codespaces, regardless of the type of project or choice of language. Everyone benefits from a nicely configured terminal! Read on to learn more.

Using the zsh Unix shell and configuring it with dotfiles

In this section, you’ll learn how to choose and configure your Unix shell and command language.

By default, when you get a codespace, you’re given access to a bash shell. However, in recent years, the zsh shell has grown in popularity, with good reason. Zsh supports pretty much everything that bash supports, with several useful additional features: it has much smarter support for tab auto-completion, it includes many commonly used aliases, and it shares command history across active shells. And so much more. This is my shell of choice when I’m developing locally, and I’d like to use it in Codespaces. Thankfully zsh is already installed in the container created by Codespaces, so all I need to do is tell VS Code that I want to use it. This can be accomplished easily with the following VS Code setting:

    "terminal.integrated.defaultProfile.linux": "zsh",

I like to set this setting using my VS Code user settings (press Ctrl + Shift + P and choose “Preferences: Open Settings (JSON)”), and I also like to have it sync across machines by enabling “Settings Sync” (go to the gear icon on the bottom left of VS Code, and choose “Turn on Settings Sync…”). That’s because I want to use zsh for every project I open, regardless of technology, but I don’t want to impose it on anyone else opening my projects on Codespaces. For more information on deciding where to add your various VS Code settings, read the “Configuring VS Code settings” section of my blog post on Configuring Azure ML projects to run on GitHub Codespaces.

The next step is to configure zsh, which you can do by adding a .zshrc file to your home directory. You could manually copy a previously configured .zshrc file to your home directory every time you start a new codespace, but that would be inefficient. Thankfully there’s a better way: dotfiles! The idea behind dotfiles is that you can create a GitHub repo with all the files you need to configure your terminal, including a main executable shell file that kicks off the process. Then, when you want to configure a terminal, you simply clone your repo to the machine you’re configuring and run the main shell file. Dotfiles are quite convenient in general, but Codespaces makes dotfiles even easier to use: instead of cloning the dotfiles repo and running the main shell file for every Docker container that Codespaces creates for you, Codespaces allows you to set your dotfiles repo path in your configuration. This way, every codespace you create automatically uses your dotfiles! This can be achieved by going to “Settings” in your GitHub user, clicking on “Codespaces,” checking the “Automatically install dotfiles” checkbox, and entering the path to your dotfiles repo:

Screenshot of the "dotfiles" section in Codespaces configuration.

When you make your own dotfiles repo, keep in mind that the repo needs to be public to work with Codespaces! The Inspiration section of the dotfiles website links to many user repos that demonstrate best practices for setting up these files. You can also take a look at my simple GitHub dotfiles repo, which I’ll discuss in the rest of this post.

There are two ways to set up your dotfiles repo for Codespaces:

  • If your dotfiles repo includes only configuration files (such as the .zshrc file), Codespaces will automatically create symlinks to those files in your home directory. This is a good option for a simple setup, with no extra terminal configuration commands that you want to run at startup. As you’ll see later, I have a few other commands I want to run, so this option does not work for me.

  • If your dotfiles repo includes an executable shell file that follows the naming conventions found in the docs, Codespaces will forego creating symlinks and will instead execute that shell file automatically whenever you create a new codespace. This is the option I use, and I chose to name my executable file install.sh. At the very least, the install.sh file should contain code that creates symlinks in your home directory to any desired configuration files. The advantage of using an install.sh file is that you now have a place to add any other commands you’d like to run at startup.

Regardless of the method you choose, when the codespace starts up, each file in the dotfiles repo is automatically cloned into /workspaces/.codespaces/.persistedshare/dotfiles. Since I chose to add an install.sh file, I now need to create symlink files (also called “symbolic links” or “soft links”) in the home directory that point to each of the dotfiles. A symlink is simply a special type of file that points to another file (and unlike a “hard link”, doesn’t require the original file to exist in the same filesystem or partition). Symlinking is better than copying, because copying unnecessarily duplicates the files: it’s a more expensive operation, it uses twice the storage, and the file copy doesn’t automatically update when the original file updates.

The code below adds symlinks to my home directory that point to all my configuration files whose names start with a dot:

https://github.com/bstollnitz/dotfiles/blob/main/install.sh
#!/bin/bash

create_symlinks() {
    # Get the directory in which this script lives.
    script_dir=$(dirname "$(readlink -f "$0")")

    # Get a list of all files in this directory that start with a dot.
    files=$(find -maxdepth 1 -type f -name ".*")

    # Create a symbolic link to each file in the home directory.
    for file in $files; do
        name=$(basename $file)
        echo "Creating symlink to $name in home directory."
        rm -rf ~/$name
        ln -s $script_dir/$name ~/$name
    done
}

create_symlinks
...

Keep in mind that the install.sh file needs to start with a ”shebang” comment indicating that it contains shell commands, as shown above. It also needs to be executable, which can be done by running the following command in the terminal:

chmod +x install.sh 

Here are the operations executed by the shell commands above:

  • We first get the directory to which the dotfiles were copied. This is /workspaces/.codespaces/.persistedshare/dotfiles when running in a codespace, but it’s best not to hardcode it. The point of dotfiles is that they can be used to setup your terminal on any machine, not just Codespaces, so they should be written in a general way. You’ll be thankful for that next time you need to repave your machine.
  • We then get a list of all files that start with a dot in the current directory. In my scenario, this list simply contains the .zshrc file to configure zsh, and a .dircolors file that we’ll talk about in a bit. When I later add more configuration files to this directory, they’ll be symlinked without requiring any changes to the code.
  • Finally, we create symlink files in the home directory that point to each of the dotfiles (after removing any files with the same name that may have already existed).

Now that I have an install.sh file that runs at startup, I can add to it any other commands that are helpful for setting up my terminal. One command that comes to mind is the following:

https://github.com/bstollnitz/dotfiles/blob/main/install.sh
...
echo "Initializing conda for zsh."
conda init zsh
...

This tells conda that I’m using the zsh shell, which enables zsh to execute conda commands.

And that’s all there is to it! In this section, you saw how to enable the zsh shell on Codespaces, and how to use dotfiles to install your configuration files. In the next couple of sections, we’ll see how to configure zsh.

I hope that you agree with me that dotfiles are super useful — they save time when you need to repave your machine, when working across machines, and now when using Codespaces!

Using the “Oh My Zsh” framework for managing zsh

Oh My Zsh is an open source, community-driven framework for managing your zsh configuration. It provides you with a wide range of plugins and themes, and it has a vibrant community of users. It’s my framework of choice for managing zsh. Fortunately, Oh My Zsh is also already installed in the Docker container created by Codespaces, which makes it very convenient to use.

There are many plugins for Oh My Zsh that you may find useful. I like the git plugin for the shorthand aliases it provides for common git commands. Here are the shell commands you need in your .zshrc file to set up Oh My Zsh, and to install the git plugin:

https://github.com/bstollnitz/dotfiles/blob/main/.zshrc
export ZSH="${HOME}/.oh-my-zsh"
...

# Plugins.
plugins=(git)

source $ZSH/oh-my-zsh.sh
...

If you want to add more plugins, simply add their names to the plugin line, separated by spaces.

Oh My Zsh also has a long list of themes to choose from. If, for example, you like the bira theme, you can use it by simply adding the following line to your .zshrc file:

ZSH_THEME=bira

The theme I use took a bit more setup to get working though. When I saw a friend use the Spaceship theme, I liked the way it formats the prompt so much that I had to have it. (Their docs say that it’s a “prompt for Astronauts.” I’m no astronaut, but I like space, which I figured was close enough.) This is what my terminal prompt looks like with the Spaceship theme:

Screenshot of my terminal prompt using the Spaceship theme.

At a glance, I know that I’m working in the fashion-mnist/managed-endpoint directory of my fashion-mnist repo (in light blue), on the main branch (in pink), with the base conda environment activated (in dark blue). I also know that I have changes on my local machine that haven’t yet been staged to be committed in git (in red).

The Spaceship theme is a custom theme that requires its own installation steps, and it makes use of the Powerline fonts, which also need to be installed. The good news is that, since I already have an install.sh file that is executed automatically by Codespaces, I can simply add extra commands to it. Here are the commands I need to install the fonts and theme:

https://github.com/bstollnitz/dotfiles/blob/main/install.sh
...
echo "Setting up the Spaceship theme."
sudo apt-get install powerline fonts-powerline -y
ZSH_CUSTOM="$HOME/.oh-my-zsh/custom"
git clone https://github.com/spaceship-prompt/spaceship-prompt.git "$ZSH_CUSTOM/themes/spaceship-prompt" --depth=1
ln -s "$ZSH_CUSTOM/themes/spaceship-prompt/spaceship.zsh-theme" "$ZSH_CUSTOM/themes/spaceship.zsh-theme"

Next I need to specify in the .zshrc file that I want to use the Spaceship theme:

https://github.com/bstollnitz/dotfiles/blob/main/.zshrc
...
ZSH_THEME="spaceship"
export SPACESHIP_DIR_TRUNC=0
...

By default, this theme truncates the prompt to display only three levels of directories, but I like to see the full path, so I set SPACESHIP_DIR_TRUNC=0. You can see all the other settings that you can use to configure the Spaceship theme in the docs.

Finally, I need to set the following settings in my user settings for VS Code, by going to the Command Palette (Ctrl + Shift + P) and choosing “Preferences: Open Settings (JSON)“:

    "terminal.integrated.fontFamily": "\"Fira Mono for Powerline\"",
    "terminal.integrated.fontSize": 13,

This tells VS Code to use one of the Powerline custom fonts in its terminal pane. Hopefully you have “Settings Sync” enabled, so that these settings sync across machines for your account.

In this section, you learned how you can use the Oh My Zsh framework to install zsh plugins and themes. In the next section, I’ll explain how I changed the color of my directory listings.

Configuring custom dir colors

When listing the contents of my current directory using la, the default colors for my directories came out displaying blue text on a green background, which I find difficult to read. This seems to be a common issue for people, so I thought I would mention it here.

One possible solution is to use a configuration file to customize the colors used for directory listings. I named mine .dircolors, and added it to my dotfiles repo, so that it gets symlinked in my home directory whenever I launch a codespace. I then copied the contents of the Solarized ansi-dark color theme to this file. And finally, I added the following code to my .zshrc to execute it:

https://github.com/bstollnitz/dotfiles/blob/main/.zshrc
...
# Set colors for LS_COLORS.
eval `dircolors ~/.dircolors`

And now my directory colors are easier to read!

Screenshot of my terminal with a list of directories and files.

We’re done with our setup, so you can now rebuild your codespace for the changes to take effect, by pressing Ctrl + Shift + P followed by “Codespaces: Rebuild Container.” Once VS Code is displayed in the browser, instead of a bash shell, a zsh shell shows up instead! Unfortunately, due to a bug in Codespaces, sometimes this shell doesn’t take into account your dotfiles configuration. Thankfully the workaround isn’t too disruptive: simply open a second zsh shell. Once you’ve done that once the changes are preserved — stopping and starting the container preserves your dotfiles configuration, and you’ll only encounter this again next time you rebuild your container.

Conclusion

In this post, you learned how to configure your GitHub Codespaces terminal using dotfiles. I explained how to configure your terminal to use zsh, Oh My Zsh, the Spaceship theme, Powerline custom fonts, and custom directory colors — but you can follow these same steps while making different choices that work for you.

Thank you for reading!