Blogging with Codespaces, part 2
I canāt believe Iāve been doing this whole blogging thing for six months now!
In those six months, my initial setup is still working well. I found a big pain point with how Iād set this up versus how I like to work though - because I put the site in my public profile repo , thereās no such thing as a private thinking. š±
Iām not comfortable enough to draft publicly - at least not for writing. Coding as a work-in-progress feels totally normal ā¦ weird, huh? Since some of these posts are five or six thousand words, the lack of drafts is getting difficult to live with! š¬ š
I want to keep the public stuff here and add another private repository to my Codespace thatāll hold my drafts, thoughts, and other inspirations for later. So letās add a private repository to our Codespace to write in, then copy over blog posts into this public repo once itās (more or less) done.
Adding another repository to a Codespace
Turns out this was trickier than expected. Iād thought that with the support for microservices and monorepos , breaking the 1:1 relationship between repository and Codespace would make it super easy. It did, just not for my use case.
I keep my drafts, notes, ideas, and other such stuff in a private repository. Itās all things vaguely related to tech, not exclusively my current job/role/employer. Iāve also turned it into a vault for Obsidian - a simple git repo and offline Markdown is great on any device. Letās add that to this blog as a private drafts store to play around with formatting, add pictures, etc., to mostly final drafts.
First, give the public repo Codespace access to the private repository. Edit the ~/.devcontainer/devcontainer.json
file to include the following (swapping).
1
2
3
4
5
6
7
8
9
10
11
12
"customizations": {
"codespaces": {
"repositories": {
"some-natalie/work-stuff": {
"permissions": {
"contents": "write",
"pull_requests": "write"
}
}
}
}
}
Next, relaunch the Codespace and grant it the new permissions (the docs , if it helps).
Now itās available to clone without having to store credentials, configure git, clone it, etc. Buuuuut, nothing is cloned by default ā¦ so Iād have to set up git and manually clone them to the same place every time. Not going to happen. š
1
2
3
4
5
6
$ ls -la /workspaces/
total 16K
drwxr-xrwx+ 5 vscode root 4.0K Apr 06 02:23 ./
drwxr-xr-x 1 root root 4.0K Apr 06 02:22 ../
drwxr-xr-x+ 4 vscode root 4.0K Apr 06 02:15 .codespaces/
drwxrwxrwx+ 11 vscode root 4.0K Apr 06 02:24 some-natalie/
Letās automatically clone every repository that we give our Codespace access to. Add a script to ~/.devcontainer/post-create.sh
that will clone each repo using the automatic git credentials that we just authorized.
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
set -e
# Get the list of other repositories from devcontainer.json using jq
REPOS=$(jq -r '.customizations.codespaces.repositories' .devcontainer/devcontainer.json | jq -r 'keys[]')
# Clone the other repos
for repo in $REPOS; do
repo_name=$(echo "$repo" | cut -d'/' -f2) # split the repo name from owner
git clone https://github.com/"$repo".git /workspaces/"$repo_name"
done
Make sure itās executable and add it to our devcontainer.json
file as (one of) the postCreateCommand
scripts.
Now the repositories are cloned on creation, but not updated in the workspace - meaning Iāll have to add them one by one to my workspace. Thatās simply too much work. This is where thereās a bit of a compromise. The .code-workspace
file that works great in VSCode on a local machine doesnāt load correctly in a Codespace (or regular devcontainer for that matter), so weāre going to add another script to do this.
Create a script called ~/load-workspace.sh
and add the following:
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
set -e
# Get the list of other repositories from devcontainer.json using jq
REPOS=$(jq -r '.customizations.codespaces.repositories' .devcontainer/devcontainer.json | jq -r 'keys[]')
# Open the other repos
for repo in $REPOS; do
repo_name=$(echo "$repo" | cut -d'/' -f2) # split the repo name from owner
code-insiders --add /workspaces/"$repo_name"
done
Swap code-insiders
for code
if you want to use the insiderās edition of VS Code instead. Weirdly enough, code
doesnāt exist at the creation/start/attach points of the lifecycle so it must be run by the user after first attach. We also want jekyll to run on every attach as well, starting our webserver.
Lucky for us, each of these lifecycle points supports parallel commands as a first class citizen. Hereās the snippet of our devcontainer.json
file showing our finished configuration:
1
2
3
4
5
6
"postCreateCommand": {
"bundle": "bundle",
"clone-repos": ".devcontainer/post-create.sh",
"first-welcome": "sudo echo '\nšŗ Run ./load-workspace.sh to add the other repositories defined in the devcontainer to VS Code. šŗ' >> /workspaces/.codespaces/shared/first-run-notice.txt"
},
"postAttachCommand": "bundle exec jekyll serve --livereload"
And now, it automatically reminds you to load everything with a single command on first launch:
Running that script reloads the Codespace window with both repositories open on launch - the public one and private one! š
If you only want to do this on your local machine without a devcontainer, the following workspace file example should get you started:
1
2
3
4
5
6
7
8
9
10
11
{
"folders": [
{
"path": "." # public repository and working directory
},
{
"path": "../work-stuff" # private repository
}
],
"settings": {}
}
Obsidian for writing
The repository for drafting, collecting thoughts, and other such goodness done in private is also an Obsidian vault kept in sync with the fabulous obsidian-git plugin. To minimize permissions, I use a fine-grained PAT to allow read/write access to the repo contents of the one repository it uses as a remote and thatās it. Since Iām mostly on a single device at any given time for writing, but read from multiple sources, this setup works great.
I add things I learned, struggled through, found inspiring, etc. to that repository. Had I thought about this more in advance, I would have had the site public and repository private. Itād allow for public content, private drafts in folder I exclude in the _config.yml
file (but still saved in the repo), and thatād be it.
š¤·āāļø Hindsight is perfect and Iām not so mad about it to redo everything.
Website setup note
This website is a free GitHub Pages site, built with Jekyll and a very popular theme (Minimal Mistakes , if youāre interested). I mostly write in Markdown anyways, so being able to mess with the site content and settings together in a Codespace is quite convenient. Itās not like writing a basic blog would ever approach the limit of the free usage entitlement, especially as thereās no want to get too fancy with the site itself. The simplicity is a feature. ā„ļø
I want to write about some of the neat stuff I build, problems to solve, and establish a public history of being something approaching competent. Iām still not wanting to dive deep into static website generation or other website stuff.
Disclosure
I work at GitHub as a solutions engineer at the time of writing this. All opinions are my own.