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
237 changes: 237 additions & 0 deletions .dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# Development Patches (.dev)

This directory contains development-only patches that enhance the local development experience but should **never be committed** to the main codebase.

## Setup (run once after cloning)

```bash
bash .dev/setup.sh
```

This adds git aliases for convenient patch management.

## Quick Start

```bash
# Check patch status
bash .dev/dev-patch.sh status

# Apply/remove ALL dev patches at once
bash .dev/dev-patch.sh all-on
bash .dev/dev-patch.sh all-off

# Apply/remove individual patches
bash .dev/dev-patch.sh on dev-login-bypass
bash .dev/dev-patch.sh off dev-login-bypass
bash .dev/dev-patch.sh toggle mock-storage
```

## Directory Structure

```
.dev/
├── README.md # This file
├── setup.sh # Run once to add git aliases
├── dev-patch.sh # Patch management script
├── patches.yaml # Patch state tracking (on/off)
├── patches/ # Patch files
│ ├── dev-login-bypass.patch
│ └── mock-storage.patch
└── hooks/ # Git hooks for safety
└── pre-commit-patch-guard.sh
```

## Available Patches

### dev-login-bypass

Adds a floating dev menu for quick user switching and creation without Google OAuth.

**Features:**
- Floating bug icon (draggable to any corner)
- Create test users with random emails/names
- Quick switch between dev users
- Toggle admin/premium status per user

**Files affected:**
- `src/components/dev-menu/dev-floating-menu.tsx` (new)
- `src/components/dev-menu/dev-user-card.tsx` (new)
- `src/components/dev-menu/random-email-generator.ts` (new)
- `src/fn/dev-auth.ts` (new)
- `src/routes/dev-login.tsx` (new)
- `src/routes/__root.tsx` (modified)
- `src/routes/api/login/google/index.ts` (modified)

**Requires:** `DEV_BYPASS_AUTH=true` in `.env`

### mock-storage

Bypasses R2/S3 storage when credentials aren't configured. Uses sample videos and images for development.

**Features:**
- No R2/S3 connection required
- Sample videos from Google Storage (~2MB each, fast loading)
- Thumbnails from Unsplash (tech/coding themed)
- Hash-based selection ensures consistent content per segment
- Console logging for storage operations

**Files affected:**
- `src/utils/storage/mock-storage.ts` (new)
- `src/utils/storage/index.ts` (modified)
- `src/fn/video-transcoding.ts` (modified)
- `src/routes/-components/hero.tsx` (modified)
- `src/utils/video-transcoding.ts` (modified)

**Requires:** `DEV_MOCK_STORAGE=true` in `.env`

## Patch Management Script

The `dev-patch.sh` script provides a unified interface for managing patches:

```bash
# Commands
bash .dev/dev-patch.sh status # Show all patches status
bash .dev/dev-patch.sh on <patch> # Apply a patch
bash .dev/dev-patch.sh off <patch> # Remove a patch
bash .dev/dev-patch.sh toggle <patch> # Toggle a patch on/off
bash .dev/dev-patch.sh all-on # Apply all patches
bash .dev/dev-patch.sh all-off # Remove all patches
bash .dev/dev-patch.sh rebuild # Rebuild patches from yaml state
bash .dev/dev-patch.sh check # Check if any patches are on (for pre-commit)
```

### How It Works

1. **State tracking**: `patches.yaml` tracks which patches are on/off
2. **Rebuild approach**: When changing patches, all files are reset to clean state, then all "on" patches are applied in order
3. **Patch ordering**: Patches are applied in the order listed in `patches.yaml` (dev-login-bypass first, then mock-storage)

## Git Aliases

After running `bash .dev/setup.sh`, these commands are available:

```bash
git dev-status # Show patch status
git dev-on # Apply all patches
git dev-off # Remove all patches
git dev-patch <cmd> # Full patch management

# Individual patches
git login-bypass-on # Apply dev-login-bypass
git login-bypass-off # Remove dev-login-bypass
git mock-storage-on # Apply mock-storage
git mock-storage-off # Remove mock-storage
```

## Pre-commit Hook Setup

The hook prevents accidentally committing when dev patches are active.

### Manual Setup

```bash
cp .dev/hooks/pre-commit-patch-guard.sh .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
```

### With Husky

Add to `.husky/pre-commit`:
```bash
.dev/hooks/pre-commit-patch-guard.sh
```

### With Lefthook

Add to `lefthook.yml`:
```yaml
pre-commit:
commands:
patch-guard:
run: .dev/hooks/pre-commit-patch-guard.sh
```

## Creating New Patches

### 1. Start from clean state

```bash
bash .dev/dev-patch.sh all-off
```

### 2. Make your dev-only changes

Edit files as needed for your new dev feature.

### 3. Generate the patch

```bash
# For tracked file changes
git diff > .dev/patches/<patch-name>.patch

# For new files, add them first
git add -N <new-file>
git diff > .dev/patches/<patch-name>.patch
```

### 4. Add to patches.yaml

```yaml
patches:
dev-login-bypass: off
mock-storage: off
your-new-patch: off # Add this line
```

### 5. Reset and test

```bash
git checkout -- .
bash .dev/dev-patch.sh on your-new-patch
```

## Troubleshooting

### Patch won't apply

```bash
# Check what's blocking
git apply --check .dev/patches/<patch-name>.patch

# Try with whitespace ignore
git apply --ignore-whitespace .dev/patches/<patch-name>.patch

# If conflicts, rebuild from clean state
bash .dev/dev-patch.sh rebuild
```

### CRLF/LF issues (Windows)

The script uses `--ignore-whitespace` to handle line ending differences. If you still have issues:

```bash
# Convert file to LF
dos2unix .dev/patches/<patch-name>.patch
```

### Patches conflict with each other

Patches are applied in order. If adding a new patch that modifies files also modified by earlier patches, make sure your patch assumes earlier patches are already applied.

### State out of sync

If `patches.yaml` says patches are on but files don't reflect it:

```bash
bash .dev/dev-patch.sh rebuild
```

### Regenerating a patch after codebase changes

If the base code changes and a patch no longer applies:

1. Start from clean state: `bash .dev/dev-patch.sh all-off`
2. Apply other patches that should come before: `bash .dev/dev-patch.sh on <earlier-patch>`
3. Manually make the changes for the broken patch
4. Generate new patch: `git diff > .dev/patches/<patch-name>.patch`
5. For new files: include them with `git diff --no-index /dev/null <file> >> .dev/patches/<patch-name>.patch`
137 changes: 137 additions & 0 deletions .dev/dev-patch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/bin/bash
# Dev Patch Manager
# Usage: .dev/dev-patch.sh <command> [patch-name]

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PATCHES_DIR="$SCRIPT_DIR/patches"
STATE_FILE="$SCRIPT_DIR/patches.yaml"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

get_all_patches() {
grep -E "^ [a-z-]+:" "$STATE_FILE" | sed 's/:.*//' | tr -d ' '
}

get_state() {
local patch="$1"
grep -E "^ $patch:" "$STATE_FILE" | sed 's/.*: //' | tr -d ' '
}

set_state() {
local patch="$1"
local state="$2"
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
}
Comment on lines +25 to +29
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

sed -i is not portable to macOS.

macOS sed requires an empty string argument for in-place editing (sed -i ''). This will fail on macOS with sed: 1: "...": extra characters at the end of command.

🔎 Proposed fix for cross-platform compatibility
 set_state() {
     local patch="$1"
     local state="$2"
-    sed -i "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    if [[ "$OSTYPE" == "darwin"* ]]; then
+        sed -i '' "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    else
+        sed -i "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    fi
 }

Alternatively, use a portable pattern that works on both:

 set_state() {
     local patch="$1"
     local state="$2"
-    sed -i "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE"
+    local tmp_file=$(mktemp)
+    sed "s/^  $patch: .*/  $patch: $state/" "$STATE_FILE" > "$tmp_file" && mv "$tmp_file" "$STATE_FILE"
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
set_state() {
local patch="$1"
local state="$2"
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
}
set_state() {
local patch="$1"
local state="$2"
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
else
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
fi
}
Suggested change
set_state() {
local patch="$1"
local state="$2"
sed -i "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE"
}
set_state() {
local patch="$1"
local state="$2"
local tmp_file=$(mktemp)
sed "s/^ $patch: .*/ $patch: $state/" "$STATE_FILE" > "$tmp_file" && mv "$tmp_file" "$STATE_FILE"
}
🤖 Prompt for AI Agents
In .dev/dev-patch.sh around lines 25 to 29, the use of sed -i is not portable to
macOS; update set_state to perform in-place replacement in a cross-platform way
(for example, call sed with a backup suffix on BSD/OSX and then remove the
backup, or detect GNU vs BSD sed and use the appropriate -i invocation, or write
the modified content to a temp file and mv it over the original). Also ensure
the $patch variable is quoted/escaped when used in the sed pattern so patch
names with special chars or spaces are handled safely.


get_patch_files() {
local patch_file="$1"
grep -E "^diff --git" "$patch_file" | sed 's|diff --git a/\([^ ]*\) b/.*|\1|'
}

reset_patch_files() {
for patch in $(get_all_patches); do
local patch_file="$PATCHES_DIR/$patch.patch"
if [ -f "$patch_file" ]; then
# Use git apply --reverse to cleanly remove patch (handles both modified and new files)
git apply --reverse --ignore-whitespace "$patch_file" 2>/dev/null || true
fi
done
}

apply_patch_raw() {
local patch="$1"
local patch_file="$PATCHES_DIR/$patch.patch"

[ ! -f "$patch_file" ] && echo -e "${RED}Patch not found: $patch_file${NC}" && return 1

git apply --ignore-whitespace "$patch_file" 2>/dev/null || \
git apply --ignore-whitespace --3way "$patch_file" 2>/dev/null || {
echo -e "${RED}Failed to apply patch: $patch${NC}"
return 1
}
}

rebuild_patches() {
reset_patch_files

for patch in $(get_all_patches); do
if [ "$(get_state "$patch")" = "on" ]; then
echo -e "${GREEN}Applying patch: $patch${NC}"
apply_patch_raw "$patch" || return 1
fi
done
}

apply_patch() {
local patch="$1"
[ "$(get_state "$patch")" = "on" ] && echo -e "${YELLOW}Patch '$patch' is already on${NC}" && return 0
set_state "$patch" "on"
rebuild_patches
}

remove_patch() {
local patch="$1"
[ "$(get_state "$patch")" = "off" ] && echo -e "${YELLOW}Patch '$patch' is already off${NC}" && return 0
set_state "$patch" "off"
rebuild_patches
}

status() {
echo "Dev Patches Status:"
echo "-------------------"
for patch in $(get_all_patches); do
local state=$(get_state "$patch")
[ "$state" = "on" ] && echo -e " ${GREEN}$patch: $state${NC}" || echo -e " $patch: $state"
done
}

check_any_on() {
grep -q ": on" "$STATE_FILE"
}

case "${1:-}" in
on) apply_patch "$2" ;;
off) remove_patch "$2" ;;
toggle) [ "$(get_state "$2")" = "on" ] && remove_patch "$2" || apply_patch "$2" ;;
Comment on lines +97 to +100
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing argument validation for patch commands.

The on, off, and toggle commands don't validate that $2 is provided. If invoked without a patch name, get_state "" and set_state "" will produce unexpected behavior.

🔎 Proposed fix
 case "${1:-}" in
-    on)      apply_patch "$2" ;;
-    off)     remove_patch "$2" ;;
-    toggle)  [ "$(get_state "$2")" = "on" ] && remove_patch "$2" || apply_patch "$2" ;;
+    on)      [ -z "${2:-}" ] && echo -e "${RED}Usage: dev-patch.sh on <patch-name>${NC}" && exit 1; apply_patch "$2" ;;
+    off)     [ -z "${2:-}" ] && echo -e "${RED}Usage: dev-patch.sh off <patch-name>${NC}" && exit 1; remove_patch "$2" ;;
+    toggle)  [ -z "${2:-}" ] && echo -e "${RED}Usage: dev-patch.sh toggle <patch-name>${NC}" && exit 1; [ "$(get_state "$2")" = "on" ] && remove_patch "$2" || apply_patch "$2" ;;
🤖 Prompt for AI Agents
In .dev/dev-patch.sh around lines 97 to 100, the case branches for on/off/toggle
do not validate that the patch-name argument ($2) is present; add an explicit
check at the start of each branch (or before the case) to ensure "$2" is
non-empty and otherwise print a concise usage/error message to stderr and exit
with non-zero status; adjust the toggle branch so it only calls get_state when
"$2" is provided; keep behavior otherwise identical (call apply_patch "$2" or
remove_patch "$2").

status) status ;;
check)
if check_any_on; then
echo -e "${RED}ERROR: Dev patches are active. Run 'git dev-off' before committing.${NC}"
status
exit 1
fi
;;
all-on)
for patch in $(get_all_patches); do set_state "$patch" "on"; done
rebuild_patches
echo ""
echo -e "${GREEN}All dev patches applied successfully!${NC}"
status
;;
all-off)
for patch in $(get_all_patches); do set_state "$patch" "off"; done
rebuild_patches
echo ""
echo -e "${GREEN}All dev patches removed successfully!${NC}"
status
;;
rebuild) rebuild_patches ;;
*)
echo "Usage: dev-patch.sh <command> [patch-name]"
echo ""
echo "Commands:"
echo " on <patch> Apply a patch"
echo " off <patch> Remove a patch"
echo " toggle <patch> Toggle a patch on/off"
echo " status Show all patches status"
echo " check Check if any patches are on (for pre-commit)"
echo " all-on Apply all patches"
echo " all-off Remove all patches"
echo " rebuild Rebuild patches from current state"
;;
esac
13 changes: 13 additions & 0 deletions .dev/hooks/pre-commit-patch-guard.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
# Pre-commit hook to prevent committing with dev patches active
# Add to .git/hooks/pre-commit or use with husky/lefthook

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEV_PATCH="$SCRIPT_DIR/../dev-patch.sh"

# Use the dev-patch check command
if [ -f "$DEV_PATCH" ]; then
bash "$DEV_PATCH" check
fi
6 changes: 6 additions & 0 deletions .dev/patches.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Dev Patches State
# Toggle patches on/off with: git dev-patch toggle <name>

patches:
dev-login-bypass: off
mock-storage: off
Loading
Loading