Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: Security

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
secrets:
name: Secret Scanning
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Scan for leaked secrets
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

shellcheck:
name: ShellCheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Lint shell scripts
uses: ludeeus/action-shellcheck@master
with:
scandir: .
ignore_names: zshrc

dangerous-patterns:
name: Dangerous Patterns
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check for destructive commands
run: |
echo "Scanning for dangerous patterns..."
EXIT_CODE=0

# Patterns to flag (with context)
PATTERNS=(
'rm -rf /'
'rm -rf ~'
'rm -rf \$HOME'
'rm -rf \*'
'mkfs\.'
'dd if='
':(){:|:&};:'
'chmod -R 777'
'chmod 777'
'> /dev/sda'
'curl.*\|.*sudo'
'wget.*\|.*sudo'
)

for pattern in "${PATTERNS[@]}"; do
# Search all tracked files, skip this workflow file itself
MATCHES=$(grep -rn --include='*.sh' --include='*.zsh' --include='zshrc' --include='*.toml' \
-E "$pattern" . \
--exclude-dir=.git \
--exclude='.github/workflows/security.yml' \
2>/dev/null || true)

if [[ -n "$MATCHES" ]]; then
echo "::warning::Found potentially dangerous pattern: $pattern"
echo "$MATCHES"
EXIT_CODE=1
fi
done

# Check for unquoted variables in shell scripts (common security issue)
UNQUOTED=$(grep -rn --include='*.sh' \
-E 'rm.*\$[A-Z_]+[^"'"'"']' . \
--exclude-dir=.git \
2>/dev/null || true)

if [[ -n "$UNQUOTED" ]]; then
echo "::warning::Found potentially unquoted variables in rm commands"
echo "$UNQUOTED"
EXIT_CODE=1
fi

if [[ $EXIT_CODE -eq 0 ]]; then
echo "No dangerous patterns found."
fi

exit $EXIT_CODE

syntax:
name: Syntax Validation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Validate bash scripts
run: |
echo "Checking bash syntax..."
bash -n install.sh
bash -n deploy.sh
echo "All bash scripts pass syntax check."

- name: Validate zsh syntax
run: |
echo "Checking zsh syntax..."
zsh -n zshrc || echo "::warning::zshrc has syntax issues (may require oh-my-zsh sourced)"
echo "Done."
14 changes: 9 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,22 @@ zsh -n zshrc
# zsh: source ~/.zshrc or open new terminal
```

### No Build/Test/Lint Commands
### CI - GitHub Actions

This repo has no build system, test suite, automated linting, or CI/CD pipeline.
A security workflow runs on push to `main` and on pull requests (`.github/workflows/security.yml`):

- **Secret scanning** - gitleaks detects leaked tokens, API keys, passwords
- **ShellCheck** - lints `install.sh`, `deploy.sh`, `zshrc` for unsafe shell patterns
- **Dangerous patterns** - flags `rm -rf /`, `chmod 777`, `curl | sudo`, etc.
- **Syntax validation** - runs `bash -n` and `zsh -n` on scripts

## File Structure

```
.
├── install.sh # Install required apps and tools
├── deploy.sh # Deploy configs to home directory
├── .github/workflows/ # CI security checks (gitleaks, shellcheck)
├── aerospace.toml # AeroSpace tiling window manager
├── config.ghostty # Ghostty terminal emulator
├── gitconfig # Git configuration
Expand Down Expand Up @@ -138,7 +144,6 @@ nvm() { _nvm_load; nvm "$@"; }
| Aliases | Short, lowercase | `gm`, `nd`, `vim` |
| Functions | snake_case | `load_nvmrc`, `_nvm_load` |
| Environment vars | UPPER_SNAKE_CASE | `NVM_DIR`, `FZF_DEFAULT_COMMAND` |
| Lua variables | snake_case | `lazy_path`, `nvmrc_node_version` |
| tmux options | kebab-case | `status-left-length`, `pane-border-status` |

## Key Architecture Decisions
Expand All @@ -156,6 +161,7 @@ nvm() { _nvm_load; nvm "$@"; }
- **Don't commit .zshenv**: Only `zshenv.example` is tracked; real secrets stay local
- **Don't modify LazyVim core**: Add overrides in `lua/plugins/`, import extras properly
- **Check deployment**: After editing, run `./deploy.sh <target>` to apply changes
- **Don't add dangerous patterns**: CI flags `rm -rf /`, `chmod 777`, `curl | sudo`, etc.

## File-Specific Notes

Expand All @@ -167,9 +173,7 @@ nvm() { _nvm_load; nvm "$@"; }
### tmux.conf
- TPM (plugin manager) installed via `./install.sh`
- After changes: `Ctrl-a + I` to install/update plugins
- Catppuccin Macchiato theme with custom status bar

### zshrc
- Profile startup: uncomment `zmodload zsh/zprof` at top and `zprof` at bottom
- Oh-My-Zsh plugins minimized for speed - add sparingly
- FZF integration uses `fd` for file finding
16 changes: 2 additions & 14 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,14 @@ A macOS dotfiles repository. Run `./install.sh` to install apps, then `./deploy.
`deploy.sh` backs up existing configs to `~/.dotfiles-backup/<timestamp>/` before overwriting.
Warns if a target app is not installed (but still copies the config).

## Config Files and Their Targets

| Repo file | Deploys to | Notes |
|---|---|---|
| `aerospace.toml` | `~/.aerospace.toml` | AeroSpace tiling WM. Reload: `alt-shift-;` then `esc` |
| `tmux.conf` | `~/.tmux.conf` | Prefix is `Ctrl-a`. Install plugins: `Ctrl-a + I` |
| `zshrc` | `~/.zshrc` | Oh-My-Zsh with lazy-loaded NVM/Conda |
| `zshenv.example` | `~/.zshenv` | Template for private tokens (never commit real values) |
| `gitconfig` | `~/.gitconfig` | Uses nvim as editor, rebase on pull |
| `config.ghostty` | Ghostty config | Catppuccin Mocha, 75% opacity |
| `nvim/` | `~/.config/nvim/` | LazyVim framework |
| `vscode-settings-user.json` | VS Code user settings | |

## Conventions

- **Theme**: Catppuccin (Mocha/Macchiato) across all tools
- **TOML formatting**: The nvim TOML formatter strips section indentation to flat style — this is expected and valid
- **Secrets**: Go in `~/.zshenv` (from `zshenv.example`), never committed. The `.gitignore` only excludes `.DS_Store`
- **Secrets**: Go in `~/.zshenv` (from `zshenv.example`), never committed
- **Commit messages**: Use conventional format with scope — `feat(tmux): ...`, `ref(zsh): ...`, `chore: ...`
- **Zsh performance**: NVM and Conda are lazy-loaded; completions are cached in `zsh/` directory. Don't add eager-loading of heavy tools
- **CI**: Security workflow runs on push/PR — gitleaks, ShellCheck, dangerous pattern scan, syntax validation

## Key Architecture Decisions

Expand Down
18 changes: 9 additions & 9 deletions deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ deploy() {
case "$target" in
zsh)
if cp "$SCRIPT_DIR/zshrc" "$HOME/.zshrc" 2>/dev/null; then
success "zsh" "zshrc" "~/.zshrc"
success "zsh" "zshrc" "$HOME/.zshrc"
DEPLOYED=$((DEPLOYED + 1))
else
fail "zsh" "failed to copy zshrc"
Expand All @@ -189,7 +189,7 @@ deploy() {
nvim)
mkdir -p "$HOME/.config"
if cp -r "$SCRIPT_DIR/nvim/" "$HOME/.config/nvim/" 2>/dev/null; then
success "nvim" "nvim/" "~/.config/nvim/"
success "nvim" "nvim/" "$HOME/.config/nvim/"
DEPLOYED=$((DEPLOYED + 1))
else
fail "nvim" "failed to copy nvim/"
Expand All @@ -199,7 +199,7 @@ deploy() {

tmux)
if cp "$SCRIPT_DIR/tmux.conf" "$HOME/.tmux.conf" 2>/dev/null; then
success "tmux" "tmux.conf" "~/.tmux.conf"
success "tmux" "tmux.conf" "$HOME/.tmux.conf"
DEPLOYED=$((DEPLOYED + 1))
else
fail "tmux" "failed to copy tmux.conf"
Expand All @@ -209,7 +209,7 @@ deploy() {

aerospace)
if cp "$SCRIPT_DIR/aerospace.toml" "$HOME/.aerospace.toml" 2>/dev/null; then
success "aerospace" "aerospace.toml" "~/.aerospace.toml"
success "aerospace" "aerospace.toml" "$HOME/.aerospace.toml"
DEPLOYED=$((DEPLOYED + 1))
else
fail "aerospace" "failed to copy aerospace.toml"
Expand All @@ -220,7 +220,7 @@ deploy() {
ghostty)
mkdir -p "$HOME/.config/ghostty"
if cp "$SCRIPT_DIR/config.ghostty" "$HOME/.config/ghostty/config" 2>/dev/null; then
success "ghostty" "config.ghostty" "~/.config/ghostty/config"
success "ghostty" "config.ghostty" "$HOME/.config/ghostty/config"
DEPLOYED=$((DEPLOYED + 1))
else
fail "ghostty" "failed to copy config.ghostty"
Expand All @@ -230,7 +230,7 @@ deploy() {

git)
if cp "$SCRIPT_DIR/gitconfig" "$HOME/.gitconfig" 2>/dev/null; then
success "git" "gitconfig" "~/.gitconfig"
success "git" "gitconfig" "$HOME/.gitconfig"
DEPLOYED=$((DEPLOYED + 1))
else
fail "git" "failed to copy gitconfig"
Expand All @@ -242,7 +242,7 @@ deploy() {
local vscode_dir="$HOME/Library/Application Support/Code/User"
mkdir -p "$vscode_dir"
if cp "$SCRIPT_DIR/vscode-settings-user.json" "$vscode_dir/settings.json" 2>/dev/null; then
success "vscode" "vscode-settings-user.json" "~/Library/.../settings.json"
success "vscode" "vscode-settings-user.json" "$HOME/Library/.../settings.json"
DEPLOYED=$((DEPLOYED + 1))
else
fail "vscode" "failed to copy vscode settings"
Expand All @@ -252,12 +252,12 @@ deploy() {

zshenv)
if [[ -f "$HOME/.zshenv" ]]; then
warn "zshenv" "skipped (${DIM}~/.zshenv already exists${RST}${YELLOW})${RST}"
warn "zshenv" "skipped (${DIM}\$HOME/.zshenv already exists${RST}${YELLOW})${RST}"
SKIPPED=$((SKIPPED + 1))
else
if cp "$SCRIPT_DIR/zshenv.example" "$HOME/.zshenv" 2>/dev/null; then
chmod 600 "$HOME/.zshenv"
success "zshenv" "zshenv.example" "~/.zshenv"
success "zshenv" "zshenv.example" "$HOME/.zshenv"
echo -e " ${YELLOW}fill in your tokens and keep chmod 600${RST}"
DEPLOYED=$((DEPLOYED + 1))
else
Expand Down
3 changes: 1 addition & 2 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ FAILED=0
if [[ -t 1 ]] && [[ -z "${NO_COLOR:-}" ]]; then
GREEN='\033[38;2;166;227;161m' # #a6e3a1
RED='\033[38;2;243;139;168m' # #f38ba8
YELLOW='\033[38;2;249;226;175m' # #f9e2af
BLUE='\033[38;2;137;180;250m' # #89b4fa
MAUVE='\033[38;2;203;166;247m' # #cba6f7
DIM='\033[38;2;108;112;134m' # #6c7086 overlay0
SUB='\033[38;2;166;173;200m' # #a6adc8 subtext0
BOLD='\033[1m'
RST='\033[0m'
else
GREEN='' RED='' YELLOW='' BLUE='' MAUVE='' DIM='' SUB='' BOLD='' RST=''
GREEN='' RED='' BLUE='' MAUVE='' DIM='' SUB='' BOLD='' RST=''
fi

# =============================================================================
Expand Down
Loading