Skip to content

Commit 11d4660

Browse files
authored
feat: add sourceenv snippet with shell recording (#398)
* chore: release 0.10.0 Release-As: 0.10.0 * feat: add sourceenv snippet with shell recording - Add sourceenv snippet demonstrating the sourceenv bash function - Include demo .env and .env.prod files showing variable sourcing - Add GIF recording of sourceenv in action - Add shell-recording Claude skill documenting shellwright workflow * feat: update recording to show cat .env before sourceenv * feat: update recording with ls -al and cat .env * docs: add README with recording instructions for sourceenv snippet * docs: add sourceenv snippet documentation page * fix: rename README.md to RECORDING.md to avoid docusaurus route conflict * feat: add ShellwrightRecording component with prompt toggle * fix: move prompt to exported const to avoid MDX parsing issues * refactor: use children prop for ShellwrightRecording prompt content * refactor: use autogenerated sidebar for snippets * refactor: move sourceenv to snippets/ to avoid double nesting * fix: add category config to link sourceenv directly * refactor: use manual sidebar with divider for flat structure * docs: add info admonition about snippet migration * feat: toggle between recording and prompt instead of showing both * fix: open Shellwright link in new tab * feat: add prompt header with Shellwright link, move credit to right * chore: remove RECORDING.md, prompt is now in index.mdx * feat: add sourceenv-prod screenshot * docs: fix TODOs in sourceenv snippet page - Add links to dotenv libraries for node and python - Add screenshot showing sourceenv output - Replace How It Works bullets with links to book chapters * docs: improve code comments in sourceenv snippet - Use proper sentences with full stops. - Add explanatory context for regex patterns and shell features. - Reference relevant book chapters for deeper learning. * chore: trigger PR preview rebuild * docs: add comments to demo .env files explaining their purpose
1 parent 7485904 commit 11d4660

File tree

11 files changed

+439
-0
lines changed

11 files changed

+439
-0
lines changed

.claude/skills/shell-recording.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
name: shell-recording
3+
description: Create shell recordings using Shellwright MCP server
4+
---
5+
6+
# Shell Recording Skill
7+
8+
Create terminal GIF recordings using the Shellwright MCP server.
9+
10+
## Prerequisites
11+
12+
- Shellwright MCP server running (configured in Claude Code settings)
13+
- `set_ps1` function available (from dwmkerr dotfiles)
14+
15+
## Recording Workflow
16+
17+
### 1. Start Shell Session
18+
19+
```
20+
shell_start with:
21+
- command: "bash"
22+
- args: ["--login", "-i"]
23+
- cols: 80
24+
- rows: 16-20 (smaller = cleaner recording)
25+
```
26+
27+
### 2. Set Clean Prompt (Before Recording)
28+
29+
Use the simplified PS1 for demos:
30+
31+
```bash
32+
set_ps1 dwmkerr_simple && cd /path/to/demo/folder && clear
33+
```
34+
35+
This shows only:
36+
- Folder path (max 3 levels)
37+
- `$` prompt
38+
39+
The `dwmkerr_simple` theme is defined in `~/repos/github/dwmkerr/dotfiles/shell.functions.d/set_ps1.sh`.
40+
41+
### 3. Start Recording
42+
43+
```
44+
shell_record_start with:
45+
- fps: 10 (good balance of quality/size)
46+
```
47+
48+
### 4. Send Commands
49+
50+
For typewriter effect (characters appear one at a time):
51+
```
52+
shell_send with:
53+
- input: "your command"
54+
- slowly: true
55+
- delay_ms: 500
56+
```
57+
58+
Then send Enter separately:
59+
```
60+
shell_send with:
61+
- input: "\r"
62+
- delay_ms: 2000 (pause before next command)
63+
```
64+
65+
### 5. Stop and Save
66+
67+
```
68+
shell_record_stop with:
69+
- name: "recording-name"
70+
```
71+
72+
Download the GIF:
73+
```bash
74+
curl -o output.gif <download_url>
75+
```
76+
77+
## Tips
78+
79+
- **2 second delays** between commands for readability
80+
- **Typewriter effect** makes commands easier to follow
81+
- **Smaller terminal** (16-20 rows) produces cleaner recordings
82+
- **Clear screen** before recording starts
83+
- **Set PS1 before recording** so setup commands aren't captured
84+
85+
## Snippet Folder Structure
86+
87+
For snippets with recordings:
88+
89+
```
90+
snippets/snippets/<name>/
91+
├── .env # Demo files
92+
├── .env.prod
93+
├── project/ # Demo folder structure
94+
│ ├── .env
95+
│ └── .env.prod
96+
├── <name>.gif # Recording
97+
├── <name>.cast # Optional asciinema cast
98+
└── init.sh # Optional setup script
99+
```
100+
101+
## Example: sourceenv Recording
102+
103+
### Folder Structure
104+
105+
```
106+
snippets/snippets/sourceenv/
107+
├── project/
108+
│ ├── .env # 4 vars: APP_URL, DATABASE_URL, LOG_LEVEL, API_TIMEOUT
109+
│ └── .env.prod # 6 vars: 2 changed + 2 new
110+
└── sourceenv.gif # Shows sourceenv then sourceenv .env.prod
111+
```
112+
113+
### Example Prompt
114+
115+
Use a prompt like this to request a shell recording:
116+
117+
```
118+
Create a shell recording using shellwright:
119+
120+
1. Scaffold folder: snippets/snippets/sourceenv/project/
121+
- .env with 4 vars (APP_URL, DATABASE_URL, LOG_LEVEL, API_TIMEOUT)
122+
- .env.prod with 6 vars (2 changed, 2 new: AWS_REGION, AUTH0_DOMAIN)
123+
124+
2. Recording settings:
125+
- Start shell in the project folder
126+
- Use simple PS1 (set_ps1 dwmkerr_simple)
127+
- Wait 2 seconds between commands
128+
- Use typewriter-style keystroke entry
129+
130+
3. Commands to record:
131+
- sourceenv (shows 4 vars set)
132+
- sourceenv .env.prod (shows 2 updated, 2 new)
133+
134+
4. Save recording to snippets/snippets/sourceenv/sourceenv.gif
135+
```

sidebarsSnippets.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
const sidebars = {
55
snippetsSidebar: [
66
'index',
7+
{ type: 'html', value: '<hr style="margin: 0.5rem 0" />' },
8+
{
9+
type: 'doc',
10+
id: 'sourceenv/index',
11+
label: 'sourceenv',
12+
},
713
],
814
};
915

snippets/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ title: 'Shell Snippets'
33
slug: '/'
44
---
55

6+
:::info
7+
8+
Each snippet on this page is being migrated to its own dedicated page - check the sidebar for individual snippet pages.
9+
10+
:::
11+
612
After finishing the [Effective Shell Book](https://amzn.to/4ho0F91) I still find myself regularly discovering or remembering techniques that are huge time-savers. I've called these **Effective Shell Snippets** and will update this page with them from time to time, so check back regularly!
713

814
### Git + AI: Interactively Staging Changes, Summarising with AI

snippets/sourceenv/index.mdx

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
title: 'sourceenv'
3+
---
4+
5+
import ShellwrightRecording from '@site/src/components/ShellwrightRecording';
6+
7+
# Source Environment Variables
8+
9+
The `sourceenv` function sources environment variables from `.env` files into your current shell session. These `.env` files are commonly used in software engineering projects to keep track of environment variables. You might see `.env.example` showing you what values you should set ([like this](https://github.com/mckinsey/agents-at-scale-ark/blob/main/services/ark-api/.env.example)) and then instruct users to copy this file to `.env` and fill with the real values - this file is then normally git ignored.
10+
11+
Many services and tools will read these files by default (there are libraries for [node](https://www.npmjs.com/package/dotenv), [python](https://pypi.org/project/python-dotenv/) and [almost anything else](https://github.com/search?q=dotenv&type=repositories)). The `sourceenv` snippet works in a very similar way - it simply sets these values in your current shell session.
12+
13+
14+
<ShellwrightRecording src={require('./sourceenv.gif').default} alt="sourceenv demo">
15+
16+
**Create a shell recording using shellwright for the sourceenv snippet:**
17+
18+
1. **Setup** (before recording starts):
19+
- Start shell in: `snippets/snippets/sourceenv/project/`
20+
- Set simple PS1: `set_ps1 dwmkerr_simple`
21+
- Clear the screen
22+
23+
2. **Recording settings:**
24+
- Terminal size: 80x20
25+
- FPS: 10
26+
- Use typewriter-style keystroke entry
27+
- Wait 2 seconds between commands
28+
29+
3. **Commands to record:**
30+
- `ls -al` (show .env files)
31+
- `cat .env` (show contents)
32+
- `clear` (execute quickly, no typewriter)
33+
- `sourceenv` (shows 4 vars set)
34+
- `sourceenv .env.prod` (shows 2 updated, 2 new)
35+
36+
4. **Save recording to:** `snippets/snippets/sourceenv/sourceenv.gif`
37+
38+
</ShellwrightRecording>
39+
40+
You can run `sourceenv` like so:
41+
42+
```bash
43+
# Just source whatever is in .env in the current directory.
44+
sourceenv
45+
46+
# Source a specific file.
47+
sourceenv .env.prod
48+
49+
# Source in verbose mode, which shows the actual values being set. Use with
50+
# caution as this will write what might be sensitive values to stdout.
51+
sourceenv -v
52+
```
53+
54+
The function shows feedback as it works:
55+
56+
![sourceenv output](./sourceenv-prod.png)
57+
58+
## The Code
59+
60+
Find the original code in my [dotfiles](https://github.com/dwmkerr/dotfiles/blob/main/shell.functions.d/sourceenv.sh). It is shown below with much more extensive documentation and explanation!
61+
62+
```bash title="https://github.com/dwmkerr/dotfiles/blob/main/shell.functions.d/sourceenv.sh"
63+
sourceenv() {
64+
local name="sourceenv"
65+
local verbose=false
66+
67+
# Parse the command line arguments using a case statement.
68+
# Read more in the chapter "Shell Script Essentials".
69+
while [[ $# -gt 0 ]]; do
70+
case $1 in
71+
-h)
72+
echo "usage: ${name} [-v] [<path_to_env_file>]"
73+
echo " Sources environment variable definitions from a .env file, e.g:"
74+
echo " KEY=VALUE"
75+
echo " -v verbose mode (show values being set)"
76+
exit 0
77+
;;
78+
-v)
79+
verbose=true
80+
shift
81+
;;
82+
*)
83+
env_file="$1"
84+
shift
85+
;;
86+
esac
87+
done
88+
89+
# Default to '.env' in the current directory if no file was specified.
90+
# Read more in the chapter "Variables, Reading Input, and Mathematics".
91+
env_file="${env_file:-.env}"
92+
93+
if [ ! -f "$env_file" ]; then
94+
echo "error: ${env_file} not found"
95+
echo "usage: ${name} [-v] [<path_to_env_file>]"
96+
else
97+
# Read the file line-by-line. The '|| [ -n "$line" ]' ensures we process
98+
# the last line even if it doesn't end with a newline.
99+
while IFS= read -r line || [ -n "$line" ]; do
100+
# Skip empty lines and lines that start with a comment.
101+
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
102+
103+
# Strip inline comments. The regex captures everything before a '#' that
104+
# isn't followed by quoted content.
105+
if [[ "$line" =~ ^([^#]*[^[:space:]])([[:space:]]*#.*)?$ ]]; then
106+
line="${BASH_REMATCH[1]}"
107+
fi
108+
109+
# Match lines that look like 'KEY=value'. The regex ensures the variable
110+
# name starts with a letter or underscore.
111+
if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then
112+
var_name="${BASH_REMATCH[1]}"
113+
var_value="${BASH_REMATCH[2]}"
114+
115+
# Strip surrounding quotes (single or double) from the value.
116+
if [[ "$var_value" =~ ^\"(.*)\"$ ]] || [[ "$var_value" =~ ^\'(.*)\'$ ]]; then
117+
var_value="${BASH_REMATCH[1]}"
118+
fi
119+
120+
# Show the user whether we're setting a new variable or updating an
121+
# existing one. The '${!var_name:-}' syntax checks if the variable is
122+
# already set. Blue for updates, green for new variables.
123+
# Read more in the chapter "Customising Your Command Prompt".
124+
if [[ -n "${!var_name:-}" ]]; then
125+
if [[ "$verbose" == true ]]; then
126+
echo -e "\033[34m${var_name}\033[0m: updated ($var_value)"
127+
else
128+
echo -e "\033[34m${var_name}\033[0m: updated"
129+
fi
130+
else
131+
if [[ "$verbose" == true ]]; then
132+
echo -e "\033[32m${var_name}\033[0m: set ($var_value)"
133+
else
134+
echo -e "\033[32m${var_name}\033[0m: set"
135+
fi
136+
fi
137+
138+
# Export the variable so it's available to child processes.
139+
export "${var_name}=${var_value}"
140+
fi
141+
done < "$env_file"
142+
fi
143+
}
144+
```
145+
146+
## How It Works
147+
148+
The code above is extensively commented to explain each step. To learn more about the shell techniques used, check out these chapters from the book:
149+
150+
- [Variables, Reading Input, and Mathematics](/part-3-manipulating-text/variables-reading-input-and-mathematics/) - how shell variables and parameter expansion work
151+
- [Shell Script Essentials](/part-4-shell-scripting/shell-script-essentials/) - functions, conditionals, and loops
152+
- [Customising Your Command Prompt](/part-5-building-your-toolkit/customising-your-command-prompt/) - ANSI escape codes for colored output
153+
154+
## Installation
155+
156+
Add the function to your shell configuration (e.g., `~/.bashrc` or `~/.zshrc`), or if you use a dotfiles setup, place it in a file that gets sourced on shell startup.
157+
158+
The latest version is available in my [dotfiles](https://github.com/dwmkerr/dotfiles/blob/main/shell.functions.d/sourceenv.sh).

snippets/sourceenv/project/.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# This is a test file used to demonstrate the 'sourceenv' snippet.
2+
# See: https://effective-shell.com/shell-snippets/sourceenv/
3+
4+
APP_URL=http://localhost:3000
5+
DATABASE_URL=postgres://localhost:5432/myapp
6+
LOG_LEVEL=debug
7+
API_TIMEOUT=30
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# This is a test file used to demonstrate the 'sourceenv' snippet.
2+
# See: https://effective-shell.com/shell-snippets/sourceenv/
3+
4+
APP_URL=https://myapp.example.com
5+
DATABASE_URL=postgres://prod.rds.amazonaws.com/myapp
6+
LOG_LEVEL=debug
7+
API_TIMEOUT=30
8+
AWS_REGION=us-east-1
9+
AUTH0_DOMAIN=myapp.auth0.com
39.9 KB
Loading

snippets/sourceenv/sourceenv.gif

2.32 MB
Loading

0 commit comments

Comments
 (0)