Skip to content
Open
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
5 changes: 3 additions & 2 deletions .env.local.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
PORT=5000
APP_HOST=http://localhost:5000
# 3000 avoids macOS AirPlay Receiver which uses 5000
PORT=3000
APP_HOST=http://localhost:3000
GITHUB_KEY=
GITHUB_SECRET=
GOOGLE_CLIENT_ID=
Expand Down
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ruby-3.3.5
3.3.5
206 changes: 206 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
.PHONY: install-prereqs check install env setup dev dev-kill test lint security db-reset db-migrate clean clean-bundle reinstall-ruby validate-network fix-ipv6 debug-network help

export PATH := $(HOME)/.rbenv/shims:$(HOME)/.rbenv/bin:$(PATH)
RBENV := export PATH="$$HOME/.rbenv/shims:$$HOME/.rbenv/bin:$$PATH" &&

# Default target - full setup to get up and running
setup: install-prereqs install env
@echo ""
@echo "== Preparing database =="
@$(RBENV) bin/rails db:prepare
@echo ""
@echo "== Clearing old logs and temp files =="
@$(RBENV) bin/rails log:clear tmp:clear
@echo ""
@echo "Setup complete! Run 'make dev' to start the development server."
@echo ""
@if command -v rbenv >/dev/null 2>&1 && ! grep -q 'rbenv init' ~/.zshrc 2>/dev/null; then \
echo "Tip: Add rbenv to your shell by running:"; \
echo " echo 'eval \"\$$(rbenv init -)\"' >> ~/.zshrc && source ~/.zshrc"; \
echo ""; \
fi

help:
@echo "Quez - Quiz App"
@echo ""
@echo "Quick start:"
@echo " make Full setup (install deps, env, database)"
@echo " make dev Start development server (web + worker)"
@echo ""
@echo "Setup:"
@echo " make install-prereqs Install Ruby, rbenv, libvips (macOS)"
@echo " make check Verify prerequisites (does not install)"
@echo " make env Copy .env.local.example to .env.local if missing"
@echo " make install Install Ruby gems"
@echo " make setup Run full setup (installs everything)"
@echo ""
@echo "Development:"
@echo " make dev Start Foreman (web server + background worker)"
@echo " make dev-kill Kill process on dev port (fixes 'Address already in use')"
@echo ""
@echo "Database:"
@echo " make db-migrate Run database migrations"
@echo " make db-reset Reset and prepare database"
@echo ""
@echo "Quality:"
@echo " make test Run test suite"
@echo " make lint Run Rubocop"
@echo " make security Run Brakeman + importmap audit"
@echo ""
@echo "Maintenance:"
@echo " make clean Remove logs, temp files, caches"
@echo " make clean-bundle Clear bundler cache (fixes git gem PathError)"
@echo ""
@echo "Troubleshooting (macOS bundle install timeout):"
@echo " make validate-network Check Ruby can reach rubygems.org"
@echo " make fix-ipv6 Set IPv6 to link-local only (fixes timeout)"
@echo " make debug-network Full diagnostics"

# Install all prerequisites (rbenv, Ruby, libvips, bundler)
install-prereqs:
@echo "== Installing prerequisites =="
@REQUIRED_RAW=$$(cat .ruby-version 2>/dev/null || echo "3.3.5"); \
REQUIRED=$${REQUIRED_RAW#ruby-}; \
\
if [ "$$(uname -s)" = "Darwin" ]; then \
if ! command -v rbenv >/dev/null 2>&1; then \
echo "Installing rbenv and ruby-build..."; \
brew install rbenv ruby-build || { echo "Error: Homebrew required. Install from https://brew.sh"; exit 1; }; \
fi; \
if ! rbenv versions --bare 2>/dev/null | grep -q "^$$REQUIRED$$"; then \
echo "Installing Ruby $$REQUIRED (this may take a few minutes)..."; \
rbenv install -s $$REQUIRED || { echo "Error: Failed to install Ruby $$REQUIRED"; exit 1; }; \
fi; \
echo "$$REQUIRED" > .ruby-version; \
rbenv rehash 2>/dev/null || true; \
if ! command -v vips >/dev/null 2>&1 && ! command -v convert >/dev/null 2>&1; then \
echo "Installing libvips for image processing..."; \
brew install vips || true; \
fi; \
else \
echo "Non-macOS detected. Please ensure Ruby $$REQUIRED, bundler, and libvips are installed."; \
echo " rbenv: https://github.com/rbenv/rbenv"; \
echo " asdf: https://asdf-vm.com/"; \
CURRENT=$$(ruby -e 'puts RUBY_VERSION' 2>/dev/null || echo "none"); \
if [ "$$CURRENT" != "$$REQUIRED" ]; then \
echo "Error: Ruby $$REQUIRED required, found $$CURRENT"; exit 1; \
fi; \
fi
@echo "Verifying Ruby..."
@$(RBENV) ruby -v && \
($(RBENV) bundle --version >/dev/null 2>&1 || (echo "Installing bundler..." && $(RBENV) gem install bundler)) && \
$(RBENV) bundle -v && \
echo "Prerequisites OK"

# Verify prerequisites (does not install)
check:
@echo "== Checking prerequisites =="
@command -v ruby >/dev/null 2>&1 || (echo "Error: Ruby not found" && exit 1)
@REQUIRED_RAW=$$(cat .ruby-version 2>/dev/null || echo "3.3.5"); \
REQUIRED=$${REQUIRED_RAW#ruby-}; \
CURRENT=$$(ruby -e 'puts RUBY_VERSION' 2>/dev/null); \
if [ "$$CURRENT" != "$$REQUIRED" ]; then \
echo "Error: Ruby $$REQUIRED required (see .ruby-version), but found $$CURRENT"; exit 1; \
fi
@ruby -v && bundle -v
@(command -v vips >/dev/null 2>&1 || command -v convert >/dev/null 2>&1) || (echo "Warning: libvips/imagemagick not found - image processing may fail" && exit 1)
@echo "All prerequisites OK"

# Copy env example to .env.local if it doesn't exist
env:
@if [ ! -f .env.local ]; then \
cp .env.local.example .env.local; \
echo "Created .env.local from .env.local.example"; \
echo "Edit .env.local to add GITHUB_KEY, GITHUB_SECRET, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET for OAuth"; \
else \
echo ".env.local already exists"; \
fi

# Install Ruby dependencies
install: install-prereqs
@echo "== Installing dependencies =="
@$(RBENV) bundle config --local timeout 300
@$(RBENV) bundle config --local retry 5
@$(RBENV) bundle check || $(RBENV) bundle install

# Start development server (Foreman: web + worker)
dev:
@echo "== Starting development server =="
./bin/dev

# Kill process on dev port - shows what's running and prompts before killing
dev-kill:
@PORT=$$(grep -E '^PORT=' .env.local 2>/dev/null | cut -d= -f2 || echo 3000); \
PID=$$(lsof -ti:$$PORT 2>/dev/null); \
if [ -z "$$PID" ]; then \
echo "No process on port $$PORT"; \
else \
echo "Process using port $$PORT:"; \
lsof -i:$$PORT 2>/dev/null | head -5; \
echo ""; \
printf "Kill it? [y/N] "; read -r reply < /dev/tty; \
case "$$reply" in [yY][eE][sS]|[yY]) \
kill $$PID 2>/dev/null || kill -9 $$PID 2>/dev/null; \
echo "Killed ($$PID)"; \
;; *) echo "Aborted"; ;; \
esac; \
fi

# Run tests
test:
bin/rails db:test:prepare test

# Run Rubocop linter
lint:
bin/rubocop

# Run security checks (Brakeman + importmap audit)
security:
@echo "== Running Brakeman security scan =="
bin/brakeman --no-pager
@echo ""
@echo "== Auditing JavaScript dependencies =="
bin/importmap audit

# Run database migrations
db-migrate:
bin/rails db:migrate

# Reset database
db-reset:
bin/rails db:reset

# Clean logs, tmp, and caches
clean:
bin/rails log:clear tmp:clear
@echo "Cleaned logs and temp files"

# Check Ruby can reach rubygems.org (detects macOS IPv6 issue)
validate-network:
@if $(RBENV) ruby -rnet/http -ruri -e 'uri=URI("https://rubygems.org/"); Net::HTTP.start(uri.host, uri.port, use_ssl: true, open_timeout: 10, read_timeout: 10) { |h| h.get("/") }; exit 0' 2>/dev/null; then \
echo "Validation OK: Ruby can reach rubygems.org"; \
elif curl -sf --connect-timeout 5 -o /dev/null https://rubygems.org/ 2>/dev/null; then \
echo "Validation FAILED: curl works but Ruby cannot reach rubygems.org"; \
echo "This is likely the macOS IPv6 issue. Fix: make fix-ipv6"; \
echo " (or System Preferences > Network > Advanced > TCP/IP > IPv6: Link-local only)"; \
echo " https://rob.co.bb/posts/2018-10-22-yak-shave-gem-install-issue/"; \
exit 1; \
else \
echo "Validation FAILED: Cannot reach rubygems.org (check network)"; \
exit 1; \
fi

# Fix macOS IPv6 issue: set to link-local only (requires sudo)
fix-ipv6:
@SERVICE="$${SERVICE:-Wi-Fi}"; \
if networksetup -listallnetworkservices 2>/dev/null | grep -q "$$SERVICE"; then \
echo "Setting IPv6 to link-local for $$SERVICE (requires sudo)..."; \
sudo networksetup -setv6LinkLocal "$$SERVICE"; \
echo "Done. Run 'make validate-network' to verify."; \
else \
echo "Service '$$SERVICE' not found. Available:"; \
networksetup -listallnetworkservices 2>/dev/null || true; \
echo ""; \
echo "Try: make fix-ipv6 SERVICE=\"Ethernet\""; \
exit 1; \
fi
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,21 @@ Run Rubocop via:
```
./bin/rubocop
```

## Troubleshooting

**Port 5000 in use**
macOS uses port 5000 for AirPlay Receiver. The default is now 3000. If you have an existing `.env.local` with `PORT=5000`, change it to `PORT=3000` and `APP_HOST=http://localhost:3000`.
[More info](https://nono.ma/port-5000-used-by-control-center-in-macos-controlce)

**bundle install times out (but curl works)**
On macOS, Ruby's Net::HTTP can hang when IPv6 is enabled.

CLI fix (recommended):
```bash
make fix-ipv6
```
Or for Ethernet: `make fix-ipv6 SERVICE="Ethernet"`

GUI fix: System Preferences → Network → Advanced → TCP/IP → Configure IPv6: **Link-local only**
[More info](https://rob.co.bb/posts/2018-10-22-yak-shave-gem-install-issue/)