Skip to content

Dynamic service composition with PostGIS support#5

Merged
landovsky merged 24 commits into
mainfrom
feature/l86-postgis-support
Feb 2, 2026
Merged

Dynamic service composition with PostGIS support#5
landovsky merged 24 commits into
mainfrom
feature/l86-postgis-support

Conversation

@landovsky

Copy link
Copy Markdown
Owner

Summary

Complete implementation of dynamic service composition for claude-sandbox, automatically detecting and provisioning only required services (Postgres/PostGIS, Redis, MySQL) in both Docker Compose and Kubernetes environments.

Feature Chain

This PR includes a series of interconnected features built in phases:

Phase 1: Service Detection (.claude-53k)

  • Detect database and cache requirements from Gemfile and config files
  • Set flags: NEEDS_POSTGRES, NEEDS_REDIS, NEEDS_MYSQL
  • Foundation for dynamic provisioning

Phase 2: Docker Compose Dynamic Profiles (.claude-4nq)

  • Pre-launch detection script (bin/detect-services.sh)
  • Dynamic profile activation based on detected services
  • Eliminates unnecessary service overhead in local development

Phase 3: K8s Dynamic Job Generation (.claude-ogp)

  • Refactor from static template to dynamic heredoc generation
  • Conditional sidecar injection based on service detection
  • Maintains reference template for documentation
  • Proper resource limits and health checks

Phase 4: PostGIS Support (.claude-l86)

  • Use postgis/postgis:16-3.4-alpine image (backward compatible)
  • Change DATABASE_URL scheme to postgis://
  • Fixes projects using PostGIS adapter (like hriste)

Additional Improvements

  • Fix hardcoded database name → use ${DATABASE_NAME} variable
  • Add imagePullPolicy: Always for fresh image pulls
  • Improve gem detection with fail-open for GitHub URLs
  • Rename .env.claude.env.claude-sandbox for clarity
  • Embed GITHUB_TOKEN in git archive URL for private repos

Files Changed

Core Implementation:

  • claude-sandbox/bin/claude-sandbox - Dynamic K8s job generation
  • claude-sandbox/bin/detect-services.sh - Service detection script (new)
  • claude-sandbox/docker-compose.yml - Profile-based service composition
  • claude-sandbox/entrypoint.sh - Service detection and logging
  • claude-sandbox/k8s/job-template.yaml - Reference template (PostGIS)

Documentation:

  • claude-sandbox/README.md - Feature documentation
  • claude-sandbox/k8s/TESTING.md - Test scenarios
  • claude-sandbox/docs/ENV-FILES.md - Updated for rename

Process Artifacts:

  • artifacts/lessons-learned.md - Captured learnings
  • .beads/issues.jsonl - Issue tracking
  • Analysis and spec artifacts

Benefits

Resource Efficiency: Only provision services actually needed by the project
Environment Parity: Same detection logic for local and remote
PostGIS Support: Projects using spatial extensions now work
Backward Compatible: Existing projects continue working unchanged
Fail-Safe: Detection failures default to provisioning all services
Maintainable: Clear separation between detection and provisioning

Testing

  • Service detection with various Gemfile patterns
  • Docker Compose profile activation
  • K8s dynamic job generation with/without sidecars
  • PostGIS projects (hriste)
  • Standard PostgreSQL projects
  • Projects with no database

Related Issues

Closes .claude-53k, .claude-4nq, .claude-ogp, .claude-l86

Statistics

  • 22 commits
  • 19 files changed
  • 3,280 insertions, 100 deletions

🤖 Generated with Claude Code

landovsky and others added 24 commits February 1, 2026 12:55
Implements Phase 1 of dynamic service composition by adding detection
functions to entrypoint.sh that analyze Gemfile and package.json to
determine required services (PostgreSQL, MySQL, SQLite, Redis).

Sets environment flags (NEEDS_POSTGRES, NEEDS_MYSQL, NEEDS_SQLITE,
NEEDS_REDIS) that will be consumed by Phase 2 (docker-compose profiles)
and Phase 3 (k8s conditional sidecars).

Detection logic:
- Scans Gemfile for pg, mysql2, sqlite3, redis, sidekiq gems
- Scans package.json for pg, mysql2, sqlite3, redis, bull, bullmq packages
- Multiple databases can be detected simultaneously
- Logs detected services and exports flags for downstream use

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements Phase 2 of dynamic service composition by adding pre-launch
service detection and Docker Compose profiles. This solves the timing
paradox where entrypoint.sh detection runs inside the container after
it's too late to decide which services Docker Compose should start.

Changes:
- Add bin/detect-services.sh for pre-launch service detection
- Add profiles to docker-compose.yml (with-postgres, with-redis, claude)
- Remove hard depends_on from claude service (apps handle retries)
- Update bin/claude-sandbox to detect services and build profile flags

The detection script analyzes Gemfile and package.json before container
starts, using git archive pattern similar to auto_detect_ruby_version.
Falls back to starting all services if detection fails or script is
missing, ensuring backward compatibility.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add documentation to claude-sandbox README explaining how the automatic
service detection works, including:
- Detection logic for Gemfile and package.json
- Pre-launch detection workflow
- Fallback behavior for git archive limitations
- Benefits of dynamic composition

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extends cmd_remote() to conditionally include postgres and redis sidecars
based on pre-launch service detection, mirroring Phase 2's Docker Compose
profile approach.

Changes:
- Add generate_k8s_job_yaml() function that creates YAML via heredoc with
  conditional blocks for sidecars
- Add service detection to cmd_remote() reusing detect-services.sh
- Replace static template approach with dynamic YAML generation
- Include DATABASE_URL/REDIS_URL env vars only when respective sidecars
  are present
- Export DATABASE_NAME for envsubst substitution
- Maintain fail-open behavior: if detection fails, include all sidecars
- Add note to job-template.yaml marking it as reference only

Benefits:
- Reduces resource waste by only provisioning needed sidecars
- Faster job startup for repos without database dependencies
- Consistent detection logic between local (docker-compose) and remote (k8s)

Testing:
- Validated YAML syntax with kubectl dry-run for all combinations
- Verified container counts match expectations (1-3 containers)
- Confirmed DATABASE_URL/REDIS_URL only present when sidecars included

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update README.md and k8s/TESTING.md to reflect the new dynamic sidecar
behavior for Kubernetes jobs.

Changes:
- Update Remote section in README to highlight dynamic sidecar feature
- Update Dynamic Service Composition section to mention K8s support
- Replace Phase 2 testing instructions with dynamic detection tests
- Add verification steps for container counts and env vars
- Add test checklist items for dynamic sidecar validation
- Document fallback behavior and git archive limitations

The documentation now accurately reflects that both local (Docker Compose)
and remote (Kubernetes) execution use the same service detection logic.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When SSH URLs are converted to HTTPS for K8s, git archive would prompt
for username/password. Now embeds the token like entrypoint.sh does.

Fixes authentication prompt when running: claude-sandbox remote

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated all references in:
- entrypoint.sh
- README.md
- docs/ENV-FILES.md
- docs/SOPS-SETUP.md
- docs/.env.claude.example -> docs/.env.claude-sandbox.example

The new name better describes the file's purpose: environment configuration
specifically for the claude-sandbox container.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Prevents K8s from using stale cached images when tags are updated.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix gem patterns to match version constraints (e.g., gem 'pg', '~> 1.6')
- Add fail-open: default to all services when git archive fails for GitHub URLs
- This ensures Rails apps always get DATABASE_URL when needed

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The static job-template.yaml contained bash-style variable syntax
${DATABASE_NAME:-sandbox_development} which could cause URI parsing errors
if accidentally used. Changed to use the literal value since this file
is marked as "REFERENCE ONLY" and the actual dynamic generation in
claude-sandbox script already handles variable substitution correctly.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes K8s job generation to use postgis/postgis:16-3.4-alpine
image and postgis:// URL scheme, making it consistent with Docker
Compose local execution. PostGIS is backward compatible with
PostgreSQL, enabling spatial database features without breaking
existing projects.

Changes:
- bin/claude-sandbox line 337: postgres:// → postgis://
- bin/claude-sandbox line 361: postgres:16-alpine → postgis/postgis:16-3.4-alpine
- k8s/job-template.yaml line 71: postgres:// → postgis://
- k8s/job-template.yaml line 100: postgres:16-alpine → postgis/postgis:16-3.4-alpine

Docker Compose already used PostGIS (lines 12, 40), so this change
ensures local/remote environment parity.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Close .claude-l86 and all subtasks (analyst, planner, implementer, reviewer)
- Add lesson about universal upgrade strategy simplifying implementation
- Capture analysis and specification artifacts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@landovsky
landovsky merged commit 3094d95 into main Feb 2, 2026
1 check passed
@landovsky
landovsky deleted the feature/l86-postgis-support branch February 2, 2026 12:29
landovsky added a commit that referenced this pull request Feb 2, 2026
The Phase 3 dynamic K8s job generation was completed and merged
in PR #5 (Dynamic service composition with PostGIS support).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant