For our bot-zero project, we've decided to go all in on dev containers. What's not to love? You can basically ship a development environment complete with node 18 and all the Visual Studio Code extensions we need. And if you don't want to use a dev container? That's also fine!
At Wehkamp my dev setup is considered a bit odd, as I love to run Windows on my Mac using Bootcamp. After 5 years still believe that my MacTel is perfectly fine to run Windows (okay... my battery does not hold out as long as on MacOs). Anyways... let's investigate how to configure our dev station with SSH keys and GPG singing in such a way that it can be reused by a DevContainer.
While this setup might be specific to Node.js, bot-zero, Windows and Wehkamp, we think it can be applied to other projects as well. We're looking forward to introduce more DevContainers within Wehkamp, so developers can just focus on what they love to do: coding.
We assume that you already have WSL2 installed.
- Intro
- Basic Git Config
- SSH setup
- GPG setup
- DevContainer
- Visual Studio Code Dev Container Extension Problems
- Convert cloned repo from HTTPS to SSH
- Further reading
- Changelog
- Comments
Basic Git Config
Before we start, let's make sure some git settings are set correctly:
git config --global user.name "My Name"
git config --global user.email "[email protected]"
# "Fix" line endings, you Linux (Docker) and Windows (host) think alike:
git config --global core.eol lf
git config --global core.autocrlf false
# Convenience, make it easier to push newly created branches
git config --global push.autoSetupRemote true
SSH setup
First, we'll need to install OpenSSH in Windows. Then we can generate a new SSH key and add it to our GitHub account.
OpenSSH install
Run the following from an elevated PowerShell (Windows Key + X, A):
# Install the OpenSSH Client
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
# Install the OpenSSH Server
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# Start the sshd service
Start-Service sshd
# OPTIONAL but recommended:
Set-Service -Name sshd -StartupType 'Automatic'
# Confirm the Firewall rule is configured. It should be created automatically by setup. Run the following to verify
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
} else {
Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
}
# Start SSH agent
Set-Service -Name ssh-agent -StartupType Automatic
Start-Service ssh-agent
This will install the OpenSSH server and client, start the SSH server automatically and allow it through the firewall. It will also start the SSH agent.
Generate new SSH key
We can continue using an normal (unelevated) PowerShell. The following command will generate new SSH key for you. Note: make sure you use the same email address as you use for your git.
ssh-keygen -t ed25519 -C "[email protected]"
You will be prompted for a passphrase. When you enter one, note that you will need to enter it every time you push or pull from Git. This will make it more secure (as only you know the passphrase), but it might make pushing and pulling a bit cumbersome.
Let's add the key to the SSH agent:
ssh-add
Let's copy the public key:
Get-Content ~/.ssh/id_ed25519.pub | Set-Clipboard; `
echo "Key copied to clipboard.";
This will copy something like ssh-ed25519 IAMASUPERLONGANDSECUREKEY [email protected] to your clipboad.
Add SSH key to GitHub
Adding a new SSH key to GitHub is very straightforward:
- Open your keys in your profile on GitHub: https://github.com/settings/profile
- Click the New SSH Key button.
- Enter a Title for the authentication key and paste the clipboard into the Key field.
- Click Add SSH Key.
Git Clone with SSH
You should be able to clone your repo using SSH, in our case we can now do the following to clone bot-zero and open it in Visual Studio Code:
git clone [email protected]:wehkamp/bot-zero.git
cd bot-zero
code .
GPG setup
Now that we've setup our SSH key, we can continue with the setup of commit verification using GPG.
Gpg4win installation
You can install Gpg4win by downloading and installing it, or install it with Chocolatey from an elevated PowerShell:
choco install gpg4win
Generate GPG key
The following steps will guide you thought the UI to create a new GPG key:
- Start Kleopatra
- Click the New Key Pair button
- Enter your Name and Email address. Tick the Protect the generated key with a passphrase box.
- Optional: disable valid until by clicking the Advanced Settings... and untick the Valid until box. Click OK.
- Click OK again.
- Enter your passphrase.
- Done.
Add GPG key to GitHub
First, we'll export the key to the clipboard:
# get the GPG key
$key=$(
gpg --list-secret-keys --keyid-format LONG `
| Select-String -Pattern "ed25519/([^ ]+)" `
| ForEach-Object { $_.Matches.Groups[1].Value } `
)
# export key
gpg --armor --export "$key" | Set-Clipboard; `
echo "Key exported to clipboard"
Now we can add it to our GitHub profile:
- Open your keys in your profile on GitHub: https://github.com/settings/profile
- Click the New GPG Key button.
- Enter a Title for the paste the clipboard into the Key field.
- Click Add GPG Key.
Local git configuration
Now let's configure git with the following PowerShell:
# get the GPG key
$key=$(
gpg --list-secret-keys --keyid-format LONG `
| Select-String -Pattern "ed25519/([^ ]+)" `
| ForEach-Object { $_.Matches.Groups[1].Value }
)
# get the location of gpg
$program=$(Get-Command gpg).Source
# config git
git config --global commit.gpgsign true
git config --global user.signingkey "$key"
git config --global gpg.program "$program"
You commits on you dev box should now be signed with the private key you just created.
Visual Studio Code GPG signing
To enable GPG signing in Visual Studio Code, you'll need to set git.enableCommitSigning
to true:
- Press F1 to open up the all commands view.
- Input >Preferences: Open Settings (UI).
- Search git.enableCommitSigning.
- Enable the Git: Enable Commit Signing to enable the commit signing.
DevContainer
Now that we've finished the setup of commit singing, let's continue with the setup of our dev container.
Dockerfile
Let's bring our own Dockerfile. Why? Docker uses layers. Installing global packages as layers will help to prevent doing installs over and over again:
# Start with the same base image
FROM mcr.microsoft.com/devcontainers/javascript-node:0-18-bullseye
# Install the npm packages globally
RUN npm install -g [email protected] \
&& npm install -g npm-check-updates
COPY ./startup.sh /
RUN chmod +x /startup.sh
So what's in here? We update NPM, to get rid of the update nags and we install npm-check-updates globally, so we can use ncu
to check if our packages should be updated. We also copy in a startup.sh
file, which will run every time we run our container.
Dev container config
Let's check our devcontainer.json:
{
"name": "Bot Zero",
"dockerFile": "Dockerfile",
"customizations": {
"vscode": {
"extensions": [
"ms-azuretools.vscode-docker",
"esbenp.prettier-vscode",
"eamodio.gitlens",
"baileyfirman.vscode-back-forward-buttons",
"streetsidesoftware.code-spell-checker",
"mikestead.dotenv",
"herrmannplatz.npm-dependency-links",
"wayou.vscode-todo-highlight",
"nsoult.typescript-imports-sort"
]
}
},
"postStartCommand": "/startup.sh",
"postAttachCommand": "bash"
}
Not much to see here. Of course we install a bunch of extensions into Visual Studio Code, which will make it easier to code. Whenever the container is started, we'll run /startup.sh
. After the startup is done, we'll open up a bash shell in which our developers can start interacting with the container.
Startup script
To make things even easier, we've added a startup script. It will configure git, install packages and copy the environment file to the right location.
#!/bin/sh
# trust the repo
# fixes:
# - fatal: detected dubious ownership in repository at '/workspaces/bot-zero'.
git config --global --add safe.directory "$PWD"
# config local GPG for signing
# fixes:
# - error: cannot run C:\Program Files (x86)\Gpg4win..\GnuPG\bin\gpg.exe: No such file or directory.
git config --global gpg.program gpg
# install NPM packages
echo ""
echo "Installing packages..."
npm install --no-audit --no-fund
# copy example.env to .env
if [ ! -f .env ]; then
echo ""
echo "Copying .example.env to .env..."
cp .example.env .env
fi
# colors
NC='\033[0m' # no color
BLUE='\033[1;34m'
YELLOW='\033[1;33m'
# echo start instructions
echo ""
echo ""
echo "${BLUE}To start your bot-zero instance, please enter: ${YELLOW}npm run dev${NC}"
echo ""
You can execute these commands by hand, but you'll need to redo this every time you rebuild your container, which isn't very user friendly.
To make this work under Windows, I needed to make sure that the line endings were setup correctly. Otherwise you'll get some weird \r
exceptions when executing the script in Docker.
Visual Studio Code Dev Container Extension Problems
You might run into some weird errors when using the latest version of the Visual Studio Code Dev Container Extension:
- Invalid mounts. As discussed here, go back to version 0.266.1 and you'll be fine.
Convert cloned repo from HTTPS to SSH
So what if you have a previously cloned your repo with HTTPS and you want to change it to SSH? You can use the following PowerShell script to change the settings:
$remoteOriginUrl = git config --get remote.origin.url; `
Write-Host "Remote url: $remoteOriginUrl" `
if(!$remoteOriginUrl.startsWith("https://")) {
Write-Host "Remote origin does not start with https://, so it can't be converted.";
exit 0;
} else {
# replace https
$remoteOriginUrl = $remoteOriginUrl.Replace("https://", "git@");
# postfix with .git
$remoteOriginUrl += ".git"
$remoteOriginUrl = $remoteOriginUrl.Replace("/.git", ".git");
$remoteOriginUrl = $remoteOriginUrl.Replace(".git.git", ".git");
# replace first slash after extension
[regex]$pattern = "(\.[^/]+)/";
$remoteOriginUrl = $pattern.replace($remoteOriginUrl, '$1:', 1) ;
git remote set-url origin $remoteOriginUrl
Write-Host "Remote url is now: $remoteOriginUrl"
}
Further reading
While researching how to set this up, I've use the following sources:
- Micrsoft Learn: Get started with OpenSSH for Windows
- GitHub Docs: Generating a new SSH key and adding it to the ssh-agent
- GitHub Docs: Adding a new SSH key to your GitHub account
- Tom Auger: How to set up signing commits and tags with git on Windows
- Ken Muse: Avoiding Dubious Ownership in Dev Containers
- StackOverflow: ssh-add returns with: "Error connecting to agent: No such file or directory"
- gpg4win.org
- Git: How to automatically create upstream branches
Changelog
- 2023-07-11: Added notes on how to enable GPG signing in Visual Studio Code.
- 2023-07-11: Added a section on the git config and on the Visual Studio Code Dev Container Extension Problems.
- 2023-07-17: Added a section on converting a repo from HTTPS to an SSH URL origin.