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
160 changes: 160 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Gratan Dev Container

This directory contains the VS Code Dev Container configuration for Gratan development.

## Quick Reference

### First Time Setup

1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop)
2. Install [VS Code Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
3. Open this repository in VS Code
4. Click "Reopen in Container" when prompted
5. Wait for build to complete
6. Run `bundle exec rspec` to verify setup

### Container Services

- **devcontainer**: Ruby development environment
- Base image: `mcr.microsoft.com/devcontainers/ruby:2`
- Workspace: `/workspace`
- Gems cached in named volume `gratan-gems`

- **mysql56**: MySQL 5.6 test instance
- Port: `14406` (host) → `3306` (container)
- Hostname: `mysql56` (within container network)
- Storage: tmpfs (ephemeral - clean state on restart)

- **mysql57**: MySQL 5.7 test instance
- Port: `14407` (host) → `3306` (container)
- Hostname: `mysql57` (within container network)
- Storage: tmpfs (ephemeral - clean state on restart)

### Running Tests

```bash
# Run all tests (MySQL 5.6)
bundle exec rspec

# Run with MySQL 5.7
MYSQL57=1 bundle exec rspec

# Run specific test
bundle exec rspec spec/change/change_grants_spec.rb
```

### Verifying MySQL Containers

```bash
# Check containers are running
docker ps

# Connect to MySQL 5.6
mysql -h mysql56 -u root

# Connect to MySQL 5.7
mysql -h mysql57 -u root
```

## Configuration Files

- **devcontainer.json**: VS Code dev container configuration
- Features: Docker-in-Docker for container inspection
- Extensions: Ruby LSP, Shopify Ruby extensions pack (auto-installed on container open)
- Settings: Format on save, Ruby LSP enabled as default formatter
- Post-create: SSH host key configuration + automatic `bundle install`
- Scripts: `.devcontainer/scripts/ensure_ssh_config.sh` for GitHub SSH persistence

- **docker-compose.yml**: Container services definition
- Networks: `gratan-dev` bridge network
- Volumes: `gratan-gems` for Bundler gem persistence (via BUNDLE_PATH)
- Health checks: Automatic MySQL readiness detection

## VS Code Extensions (Auto-Installed)

When you open this project in the dev container, the following extensions are automatically installed:

- **Shopify.ruby-lsp**: Modern Ruby language server for IntelliSense, go-to-definition, and diagnostics
- **Shopify.ruby-extensions-pack**: Ruby syntax highlighting and snippets

### Verifying Extensions Work

1. **Formatting on save**: Edit any `.rb` file, make a change, and save (Cmd/Ctrl+S). Ruby LSP will format the file automatically.
2. **IntelliSense**: Type a Ruby keyword or method name - you should see autocomplete suggestions.
3. **Go to definition**: Cmd/Ctrl+Click on a method name to jump to its definition.
4. **Test Explorer** (optional): Install "Ruby Test Explorer" extension manually if you want UI-based test running.

## Troubleshooting

### Container won't start

1. Ensure Docker Desktop is running
2. Check Docker has sufficient resources (4GB+ RAM recommended)
3. Rebuild container: Command Palette → "Dev Containers: Rebuild Container"

### Tests fail to connect to MySQL

1. Verify MySQL containers are running: `docker ps`
2. Check health status: `docker ps --format '{{.Names}}: {{.Status}}'`
3. Wait for health checks to pass (up to 30 seconds after start)

### Gems not installing

1. Check network connectivity
2. Clear gem cache: Remove `gratan-gems` volume and rebuild
3. Run manually: `bundle install --verbose`

### Slow build times

- **First build**: ~5 minutes (downloading base images)
- **Incremental rebuild**: <2 minutes (cached layers)
- **Tip**: Use "Rebuild Container" instead of "Rebuild Without Cache" unless necessary

## Advanced Configuration

### Custom Ruby Version

Edit `devcontainer.json` to specify exact Ruby version:

```json
{
"features": {
"ghcr.io/devcontainers/features/ruby:1": {
"version": "2.7"
}
}
}
```

### Port Conflicts

If ports 14406 or 14407 are already in use:

1. Edit `docker-compose.yml`
2. Change port mappings (e.g., `"14408:3306"`)
3. Update test configuration in `spec/spec_helper.rb`

### Persistent Test Databases

By default, MySQL uses tmpfs (ephemeral storage). To persist data:

1. Edit `docker-compose.yml`
2. Remove `tmpfs:` sections from mysql56/mysql57
3. Add volume mounts instead:
```yaml
volumes:
- mysql56-data:/var/lib/mysql
```

4. Define volumes at top level:
```yaml
volumes:
mysql56-data:
mysql57-data:
```

## More Information

- [Full Quickstart Guide](../specs/001-devcontainer/quickstart.md)
- [VS Code Dev Containers Documentation](https://code.visualstudio.com/docs/devcontainers/containers)
- [Docker Compose Documentation](https://docs.docker.com/compose/)
30 changes: 30 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "Gratan Development",
"dockerComposeFile": "docker-compose.yml",
"service": "devcontainer",
"workspaceFolder": "/workspace",

"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},

"customizations": {
"vscode": {
"extensions": [
"Shopify.ruby-lsp",
"Shopify.ruby-extensions-pack"
],
"settings": {
"ruby.lsp.enabled": true,
"editor.formatOnSave": true,
"[ruby]": {
"editor.defaultFormatter": "Shopify.ruby-lsp"
}
}
}
},

"postCreateCommand": "bash .devcontainer/scripts/ensure_ssh_config.sh && bundle install",

"remoteUser": "vscode"
}
62 changes: 62 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
services:
devcontainer:
image: mcr.microsoft.com/devcontainers/ruby:2
volumes:
- ..:/workspace:cached
# Persist Bundler gems for faster rebuilds
- gratan-gems:/usr/local/bundle
command: sleep infinity
working_dir: /workspace
networks:
- gratan-dev
depends_on:
mysql56:
condition: service_healthy
mysql57:
condition: service_healthy
environment:
- BUNDLE_PATH=/usr/local/bundle
- MYSQL_HOST_56=mysql56
- MYSQL_HOST_57=mysql57
- MYSQL_PORT=3306

mysql56:
image: mysql:5.6
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
ports:
- "14406:3306"
networks:
- gratan-dev
tmpfs:
- /var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
start_period: 30s

mysql57:
image: mysql:5.7
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
ports:
- "14407:3306"
networks:
- gratan-dev
tmpfs:
- /var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
start_period: 30s

networks:
gratan-dev:
driver: bridge

volumes:
gratan-gems:
30 changes: 30 additions & 0 deletions .devcontainer/scripts/ensure_ssh_config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail

SSH_DIR="$HOME/.ssh"
CONFIG_FILE="$SSH_DIR/config"
KNOWN_HOSTS="$SSH_DIR/known_hosts"

mkdir -p "$SSH_DIR"
chmod 700 "$SSH_DIR"

grep -q "Host github.com" "$CONFIG_FILE" 2>/dev/null || {
cat >> "$CONFIG_FILE" <<'EOF'
# Added by devcontainer postCreateCommand for persistent GitHub SSH handling
Host github.com
StrictHostKeyChecking accept-new
UserKnownHostsFile ~/.ssh/known_hosts
EOF
}

# Refresh github.com keys (avoid duplicates by removing existing lines first)
if [ -f "$KNOWN_HOSTS" ]; then
grep -v 'github.com' "$KNOWN_HOSTS" > "$KNOWN_HOSTS.tmp" || true
mv "$KNOWN_HOSTS.tmp" "$KNOWN_HOSTS"
fi

ssh-keyscan github.com >> "$KNOWN_HOSTS" 2>/dev/null || echo "[warn] ssh-keyscan failed"
chmod 600 "$CONFIG_FILE"
chmod 600 "$KNOWN_HOSTS"

echo "[info] SSH config for github.com ensured (accept-new host key policy)."
31 changes: 31 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# gratan Development Guidelines

Auto-generated from all feature plans. Last updated: 2025-11-11

## Active Technologies
- MySQL 5.6/5.7 (ephemeral containers on ports 14406/14407) (001-devcontainer)

- Ruby 2.x+ (match existing .ruby-version or gemspec) + Docker, Docker Compose, VS Code Dev Containers extension (001-devcontainer)

## Project Structure

```text
src/
tests/
```

## Commands

# Add commands for Ruby 2.x+ (match existing .ruby-version or gemspec)

## Code Style

Ruby 2.x+ (match existing .ruby-version or gemspec): Follow standard conventions

## Recent Changes
- 001-devcontainer: Added Ruby 2.x+ (match existing .ruby-version or gemspec) + Docker, Docker Compose, VS Code Dev Containers extension

- 001-devcontainer: Added Ruby 2.x+ (match existing .ruby-version or gemspec) + Docker, Docker Compose, VS Code Dev Containers extension

<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->
Loading