dotfiles icon indicating copy to clipboard operation
dotfiles copied to clipboard

Computer setup and settings. Apple Silicon ready.

Dotfiles

Brendon Smith (br3ndonland)

Table of Contents

  • Overview
    • What
    • Why
    • How
  • Hardware
  • macOS
  • Homebrew package management
  • Shell
  • Text editors
    • VSCode desktop
    • VSCode browser
  • Fonts
  • Language-specific setup
    • JavaScript
    • Python
  • PGP
    • GPG
    • Keybase
    • Proton Mail
  • SSH
    • Key generation
    • Connecting to GitHub
    • SSH agent forwarding
    • 1Password SSH features
  • General productivity
  • Media
  • Science

Overview

What

This repo contains dotfiles, which are application configuration and settings files. They frequently begin with a dot, hence the name. Dotfiles are compatible with Linux and macOS.

Why

  • Make developer environments automated and disposable. Disposability is an important concept in infrastructure-as-code DevOps, serverless computing, CI/CD, and more recently, in-browser development environments. Why aren't developers applying automation and disposability to their own computers? With an automated disposable developer environment, setup of a new machine is fast and easy. This approach is also liberating - I can purchase a new computer (or wipe an existing one), run bootstrap.sh, and be up and running again in no time.
  • Know when and why settings change. I not only know what tools and settings I'm using, but when and why I chose the tools and settings. This has been particularly important for VSCode, because settings change (and break) frequently, and it helps to record troubleshooting info in the Git log.
  • Learn new skills. I learn skills, like shell scripting, that are useful and don't go out of date quickly. I wouldn't know shell as well if I didn't work on my developer environment. I learn these skills by tinkering a little bit at a time, in an unstructured way. It's time I might not otherwise be writing code.

How

This dotfiles repository is meant to be installed by bootstrap.sh.

bootstrap.sh is a shell script to automate setup of a new macOS or Linux development machine. It is idempotent, meaning it can be run repeatedly on the same system. To set up a new machine, simply open a terminal and run the following command:

STRAP_GIT_EMAIL="[email protected]" STRAP_GIT_NAME="Your Name" STRAP_GITHUB_USER="username" \
  /usr/bin/env bash -c "$(curl -fsSL https://raw.githubusercontent.com/br3ndonland/dotfiles/HEAD/bootstrap.sh)"

The following environment variables can be used to configure bootstrap.sh, and should be either set before with export, or inline within the command to run the script:

  • STRAP_GIT_EMAIL: email address to use for Git configuration. Will error and exit if not set.
  • STRAP_GIT_NAME: name to use for Git configuration. Will error and exit if not set.
  • STRAP_GITHUB_USER: username on GitHub or other remote from which dotfiles repo will be cloned. Defaults to my GitHub username, so you should set this if you're not me.
  • STRAP_DOTFILES_URL: URL from which the dotfiles repo will be cloned. Defaults to https://github.com/$STRAP_GITHUB_USER/dotfiles, but any Git-compatible URL can be used, so long as it is accessible at the time the script runs.
  • STRAP_DOTFILES_BRANCH: Git branch to check out after cloning dotfiles repo. Defaults to main.

bootstrap.sh will set up macOS and Homebrew, run scripts in the scripts/ directory, and install Homebrew packages and casks from the Brewfile.

A Brewfile is a list of Homebrew packages and casks (applications) that can be installed in a batch by Homebrew Bundle. The Brewfile can even be used to install Mac App Store apps with the mas CLI. Note that you must sign in to the App Store ahead of time for mas to work.

Hardware

macOS

  • macOS setup is automated with macos.sh.

  • Karabiner Elements is used for keymapping.

    • Settings are stored in .config/karabiner/karabiner.json. Note that Karabiner will auto-format the JSON with four spaces. To avoid changing the formatting with the Prettier autoformatter, I added karabiner.json to .prettierignore.

    • Simple modifications:

      From key To key
      caps_lock escape
      escape caps_lock
    • Complex modifications:

      • Launch Terminal with Cmd+Escape
      • See karabiner.json for more.
    • Devices

      • Disable built-in keyboard when external keyboard is connected

Homebrew package management

  • Homebrew is a package manager that includes Homebrew-Cask to manage other macOS applications.

  • See the Homebrew docs for further info.

  • My list of Homebrew packages and casks is available in my homebrew-brewfile repo, and engineered for use with strap.

  • The Brewfile works with Homebrew Bundle to manage all Homebrew packages and casks together.

  • Key Brew Bundle commands:

    # Install everything in the Brewfile
    brew bundle install --global
    # Check for programs listed in Brewfile
    brew bundle check --global
    # Remove any Homebrew packages and casks not in Brewfile
    brew bundle cleanup --force --global
    

Shell

Text editors

VSCode desktop

I write code with VSCodium, an alternate build of Microsoft Visual Studio Code (VSCode) that is free of proprietary features and telemetry. To work on servers via SSH, I use Pony SSH.

I previously configured VSCode and VSCodium using the Settings Sync extension. I now have my settings, keybindings, and extensions stored here in my dotfiles repo. Extensions can be installed by running vscode-extensions.sh along with the name of the editor, like vscode-extensions.sh codium. The shell script was quite easy to write. I based it on npm-globals.sh, and used the VSCode extension CLI.

VSCode browser

codespaces

I have tried GitHub's cloud-hosted VSCode, called Codespaces. So far, I don't like it because:

  • Keybindings conflict with browser keybindings. The command palette may not be able to be opened with the usual keybindings, because Cmd+shift+p is already in use by the browser. This was partially addressed by the VSCode extension, which allows remote development from outside the browser.
  • Lag. There's lag between when a character is typed and when it appears. This can be very bothersome.
  • Files. It's unclear if or how persistent file storage is available. Local filesystem access is limited.
  • GPG commit signing has some limitations. GitHub offers commit signing with GPG, but private keys must be in GitHub's custody.
  • Customization is complicated.
    • Codespaces doesn't automatically clone or run these dotfiles. They claim that codespaces will automatically clone your dotfiles and run bootstrap.sh, but the repo is not cloned to ~/.dotfiles, and bootstrap.sh is not running, even after enabling the "Automatically install dotfiles" setting in user preferences.
    • Codespaces can't read VSCode settings from dotfiles. The docs explain, "Currently, Codespaces does not support personalizing the User settings for the Visual Studio Code editor with your dotfiles repository." The separate Settings Sync service is required to sync settings.
    • Extensions have to be managed separately from VSCode desktop. There is a "github.codespaces.defaultExtensions" setting that requires its own list of extensions, and it's unclear if Codespaces can install extensions from VSIX (like Dracula Pro).
    • Codespaces may not be able to install and use custom fonts (like Dank Mono). Font capabilities may be limited by the browser.
    • For further customization, Codespaces supports "dev containers," which are created with special Dockerfiles in each project. This adds further complication and maintenance overhead.
  • Codespaces pricing requires enterprises to set up separate spending limits, and is not specified for public repos on free plans. Do you want to pay for your text editor by the minute?

code-server

  • code-server is an open-source alternative to codespaces.

  • I set up code-server on a Linux cloud server. I prefer to use DigitalOcean, following their recommended initial setup guide for "droplets" (VMs):

    • Set up SSH agent forwarding on local machine to avoid having to deposit SSH private keys on droplet
    • Add SSH public key when creating droplet
    • Add a user data script like linux-userdata.sh
  • Installation and setup on the server:

    curl -fsSL https://code-server.dev/install.sh | sh
    sudo systemctl enable --now code-server@$USER
    
  • Server configuration file: note that user-data-dir must be an absolute path if running with the systemctl background service.

    bind-addr: 127.0.0.1:8080
    auth: none
    password: false
    cert: false
    user-data-dir: /home/brendon/.dotfiles/vscode
    
  • Local machine

    • Forward port from server by running ssh -N -L 8080:127.0.0.1:8080 code-server on the local machine.
    • Open http://localhost:8080 in a browser and you should see VSCode!
    • Multiple workspaces can be opened by passing the ?workspace= query parameter in the URL. Each browser tab can have a workspace open.
    • Proxy ports back to local machine with the /proxy endpoint. For example, to hit an API endpoint running on port 1025 on the server, curl :8080/proxy/1025. As explained in the VSCode docs, if developing on Linux, note that non-root users may not be able to expose ports less than 1024. The port is set to 1025 in the debugger config for this reason.
  • Extensions:

    • code-server has its own extension marketplace that is created by scraping GitHub.
    • You can also install extensions with the CLI: code-server --install-extension.
  • Settings

    • If the shell doesn't look right: Command Palette -> Terminal: Select Default Shell
    • The browser may grab some VSCode keybindings. I prefer to use Safari, because it grabs the least shortcuts.
    • code-server can't be used as a Git editor, as far as I can tell. It can open text files from the command-line, but the --wait switch is not recognized.
    • The clipboard doesn't completely work. See coder/code-server#1106.
    • The font can't yet be customized directly. See coder/code-server#1374.
    • You can change the color theme, but you may need to re-select the theme each time you open a browser tab.

Fonts

Dank Mono is my programming font of choice. The rounded characters are eminently readable, the italics are elegant, and the ligatures are intuitive. Recursive Mono, Fira Code, and Ubuntu Mono are decent free alternatives.

Language-specific setup

JavaScript

  • node is a JavaScript runtime used to run JavaScript outside of a web browser.
  • npm is a package manager written in node.js, included when node is installed.
    • It's difficult to keep track of global npm packages. There's no easy way to do it with the usual package.json. As Isaac Schlueter commented in 2012,

      Yeah, we're never going to do this.

    • Instead, packages can be installed with Homebrew, or with npm-globals.sh.
    • npm-check can be used to manage global packages after install, with npm-check -ug. If not using npm-check, a list of global npm packages can be seen after installation with npm list -g --depth=0.
  • I use the Prettier autoformatter and the Prettier VSCode extension to format my web code, including JavaScript and Vue.js. Prettier is an extremely helpful productivity tool, and I highly recommend it. Autoformatters save time and prevent bikeshedding.
  • Compared with Prettier, ESLint formats less code languages, requires complicated setup, and doesn't work well when installed globally.
  • In the past, I also used JavaScript Standard Style (aka StandardJS). Standard Style has also reportedly been favored by Brendan Eich (creator of JavaScript) and Sir Tim Berners-Lee (creator of the World Wide Web). Prettier provides a similar code style, but with more features, so I use Prettier instead.

Python

  • I format Python code with Black.
    • VSCode provides built-in support for Black. I set VSCode to autoformat on save.
    • Black is still considered a pre-release.
    • If you prefer the less-decisive PEP 8 format, I recommend autopep8 for autoformatting. VSCode also has built-in Python formatting support for autopep8.
  • See my template-python repo for useful tooling and additional sensible defaults.

PGP

  • I use Gnu Privacy Guard (GPG, the free implementation of Pretty Good Privacy (PGP)), Keybase, and Proton Mail to encrypt and share messages, passwords, and other sensitive info.
  • PGP vs SSL: SSL/TLS/HTTPS encrypts data in transit, but the storage provider like Dropbox, Google, or Slack can still read it. Communications which are end-to-end PGP encrypted can only be read by the sender or recipient, never the provider.

GPG

GPG is an implementation of OpenPGP.

Installation

  • Install gnupg:
    • macOS: brew install gnupg
    • Ubuntu Linux: sudo apt-get install gnupg
    • Manually: Download from GnuPG
  • Install pinentry:
    • macOS: brew install pinentry (terminal) or brew install pinentry-mac (app from GPGTools suite that enables use of macOS keychain and GUI apps like VSCode)
    • Ubuntu Linux: apt install pinentry
    • Manually: On macOS, Apple's command-line tools (xcode-select) should have the build prerequisites. Download libgpg-error, libassuan, and pinentry, then build from source and install in order (libgpg-error, then libassuan, then pinentry):
      cd /path/to/libgpg-error
      ./configure; make; sudo make install
      cd /path/to/libassuan
      ./configure; make; sudo make install
      cd /path/to/pinentry
      ./configure; make; sudo make install
      
  • Set the pinentry program:
    • ~/.gnupg/gpg-agent.conf: pinentry-program /opt/homebrew/bin/pinentry
    • The path apparently has to be absolute, so it may vary by system.
    • See the GPG agent options docs for more.
  • Export the GPG_TTY environment variable: export GPG_TTY=$(tty) (can add to shell profile to automatically export). Note that this is not the same thing as export GPG_TTY=$TTY, which may raise cryptic Inappropriate ioctl for device errors.
  • Ensure proper permissions are set on GPG config files:
    chmod 700 ~/.gnupg
    chmod 600 ~/.gnupg/gpg.conf
    
  • See the GPG configuration docs for more.

GPG and YubiKey

Resources:

To use a GPG key on a YubiKey with a new computer, plug in the YubiKey, check the status, and fetch the public keys.

~
❯ gpg --card-status

~
❯ gpg --card-edit

gpg/card> admin
Admin commands are allowed

gpg/card> url
URL to retrieve public key: https://github.com/<YOUR_GITHUB_USERNAME>.gpg

gpg/card> fetch

gpg/card> quit

GPG key generation

  • Run gpg --full-generate-key from the command line to generate a key. Respond to the command-line prompts. The maximum key size of 4096 is recommended.
  • View keys with gpg --list-secret-keys.
  • Run gpg --send-keys <keynumber> to share the public key with the GPG database. It takes about 10 minutes for the key to show up in the GPG database.

Key import and export

  • Import a GPG key from a file: gpg --import /path/to/privatekey.asc
  • Export your GPG public key:
    • Copy to clipboard (for pasting into GitHub/GitLab): gpg --armor --export | pbcopy
    • Export to a file: gpg --armor --export > public.gpg

Sending messages

  • Locate another user's key in the global database with gpg --search-keys <email>.
  • Encrypting communications
    • Encrypt a message with echo "Hello, World!" | gpg --encrypt --armor --recipient "<email>". Optionally, save the encrypted message in a .gpg file.
    • If the message was saved in a file, send the file over email, Slack, or any other medium.
    • Decrypt the message with gpg --decrypt.
      • If copying the encrypted text directly, include it in quotes: echo "BIG LONG GPG STRING" | gpg --decrypt.
      • If reading a file, include the filename when decrypting: gpg --decrypt message.gpg.
      • Decrypted output can be autosaved to a new file: gpg --decrypt message.gpg --output file.txt.

Signing Git commits with GPG

Note that SSH can also be used to sign Git commits. See the SSH section for further details.

  • See Pro Git: Signing your work.
  • Install and configure pinentry as described above.
  • Configure Git to use GPG and your key for commits, using .gitconfig:
    • Set signingkey: git config --global user.signingkey 16digit_PGPkeyid the 16 digit PGP key id is the partial 16 digit number listed on the sec line).
      [user]
      name = your name
      email = [email protected]
      signingkey = 16digit_PGPkeyid
      
    • Turn on gpgsign:
      [commit]
      gpgsign = true
      

General

  • Restart the agent: gpgconf --kill gpg-agent or gpgconf --kill all. See the GPG docs on invoking gpg-agent.
  • Verify GPG signing capabilities: echo "test" | gpg --clearsign
  • Trust GPG keys using the GPG TTY interface:
    • If you see gpg: WARNING: This key is not certified with a trusted signature! when examining signed Git commits with git log --show-signature, you may want to trust the keys.
    • Enter the GPG key editor from the command line with gpg --edit-key <PGPkeyid>.
    • Set trust level for the key by typing trust, hitting enter, and entering a trust level.
    • See the GPG docs for more info.
  • GitHub GPG instructions
  • GitLab GPG instructions
  • If working on a server, you can use ssh agent forwarding to access your SSH and GPG keys without having to copy them.

Keybase

Zoom acquisition

Keybase was acquired by Zoom, and its future is uncertain.

Useful features

  • PGP key and identity management
    • Keybase solves the key identity problem. Even if you have someone's public PGP key, you can't verify it actually came from them unless you exchange it in person. Keybase provides a unified identity for verification of PGP keys. Each device gets its own private key, and they share identity. It was previously challenging to move PGP keys among devices, but now it can be accomplished simply by signing in to Keybase.
    • Following someone is a way of verifying their cryptographic identity, not a way of subscribing to updates from the person like social media.
    • PGP key features are available through the CLI with keybase pgp commands.
  • Chat
  • Teams
  • Encrypted files
    • The Keybase file system (KBFS) is like an encrypted Dropbox or Google Drive cloud storage system.
    • Integrates with the macOS finder through use of the FUSE for macOS package.
  • Crypto tools
  • Git
    • Keybase allows users and teams to create and store end-to-end encrypted Git repositories. See the Keybase Git docs and Keybase Git blog post.
    • Treat Keybase Git repos as remotes (like GitHub repos). They can be cloned, pushed, and pulled, as you would do for GitHub repos. For example: git remote add keybase keybase://$PUBLIC_OR_PRIVATE/$USERNAME/$REPONAME.
    • As of Keybase 5.1.0, Git LFS is also enabled.
    • Keybase can't be used to sign Git commits. The best method is to export your PGP key from Keybase to GPG, and then sign Git commits with GPG.
    • There has been some debate about the need to sign Git commits at all. Linus Torvalds has recommended the use of git tag -s to sign with tags instead. The Keybase developers sign releases with tags, but don't always sign commits to the Keybase source code. However, in order to sign tags, you still need to set up commit signing, so why not just sign commits also? Whether you sign all commits or just tags, Keybase should improve this feature.

Proton Mail

I use Proton Mail for PGP-encrypted email.

SSH

Key generation

To generate an SSH key:

ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/id_ed25519_"$(id -un)"

If you have a FIDO2 security key that supports discoverable credentials (formerly known as resident keys), such as a YubiKey, you can generate an SSH key that is stored directly on the FIDO2 hardware device.

You'll need libfido2 and OpenSSH version 8.2 or later.

brew install libfido2 openssh

Next, generate a key with the -O resident option. You can additionally set a PIN on the YubiKey, which requires the YubiKey Manager CLI or GUI, and require PIN verification for use of the SSH key, with the -O verify-required option. In this scenario, the SSH key itself does not need a password. The password is replaced by the YubiKey and its PIN.

ssh-keygen -t ed25519-sk -O resident -O verify-required -C "[email protected]" -f ~/.ssh/id_ed25519_"$(id -un)"

Connecting to GitHub

See the GitHub docs on connecting to GitHub with SSH.

GitHub supports use of SSH keys from FIDO2 security key hardware devices like YubiKeys. See the GitHub docs, GitHub blog, and Yubico blog.

GitHub also supports use of SSH keys for signing Git commits. See the GitHub changelog and GitHub docs. See the 1Password section for instructions.

SSH agent forwarding

If working on a server, you can use ssh agent forwarding to access your SSH and GPG keys without having to copy them.

Host yourserver.com
  ForwardAgent yes

1Password SSH features

1Password includes features for managing SSH keys. At this time, SSH features are limited to your Personal vault.

To get started:

  • Generate or import an SSH key
  • Upload the key to GitHub or any platform to which you connect with SSH
  • Turn on the 1Password SSH agent
  • Update the SSH config to use the 1Password IdentityAgent
  • Optionally, simplify the agent path by creating a symlink to ~/.1password/agent.sock.

1Password also supports Git commit signing with SSH keys. See the 1Password blog and GitHub changelog.

To sign Git commits with SSH and 1Password:

  • Tell GitHub about the SSH key:
    • Go to https://github.com/settings/keys
    • Click "New SSH key"
    • Select the key type "signing key"
    • Allow the 1Password browser extension to autofill the "key" input field with an SSH public key. Either generate a new SSH key with 1Password or use an existing one. The same SSH key can be used for both authentication and signing.
  • Tell Git about the SSH key:
    • Set git config gpg.format = ssh
    • Set git config gpg.ssh.allowedsignersfile=~/.ssh/allowed_signers
    • Set git config gpg.ssh.program=/Applications/1Password.app/Contents/MacOS/op-ssh-sign
    • Set git config user.signingkey to the SSH public key
    • Create the file ~/.ssh/allowed_signers (when using this repo, will be symlinked from ~/.dotfiles/.ssh/allowed_signers)
    • For each signing key, add a single line to the ~/.ssh/allowed_signers specifying the combination of git config user.email and git config user.signingkey, in that order

General productivity

Media

Science

(Back to top)