-
Notifications
You must be signed in to change notification settings - Fork 20
Add Playground Apps Manager - Web-based devcontainer app deployment system #294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
024e4b6 to
9767e9c
Compare
9767e9c to
c6c7056
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces the Playground Apps Manager, a web-based system enabling users to create, manage, and deploy custom devcontainer-based applications with real-time monitoring and build log streaming.
Changes:
- Full-stack web application with Go backend and vanilla JavaScript frontend for CRUD operations on containerized apps
- Integration with Docker and Caddy for container lifecycle management and reverse-proxy routing
- PostgreSQL database for persistent app storage with automatic Caddy route synchronization on startup
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/playground/playground/main.go | Application entry point configuring database, Docker, Caddy clients and HTTP routing |
| src/playground/playground/handlers.go | HTTP handlers for app CRUD operations, container control, and log viewing |
| src/playground/playground/models.go | Data structures for apps, requests, and responses |
| src/playground/playground/validation.go | Input validation for app creation including name format and port range checks |
| src/playground/playground/database.go | PostgreSQL database layer with schema initialization and CRUD operations |
| src/playground/playground/docker.go | Docker client for container operations via devcontainer CLI |
| src/playground/playground/docker_service.go | Service layer orchestrating Docker operations and database updates |
| src/playground/playground/caddy.go | Caddy Admin API client for dynamic route management |
| src/playground/playground/caddy_service.go | Service layer for Caddy route synchronization with status updates |
| src/playground/playground/constants.go | Configuration constants and templates for devcontainer generation |
| src/playground/playground/static/index.html | Frontend UI for app management with real-time status updates |
| src/playground/playground/go.mod | Go module definition with PostgreSQL driver dependency |
| src/playground/playground/Dockerfile | Multi-stage build with devcontainer CLI and ttyd installation |
| src/playground/docker-compose.yaml | Service definitions for Caddy, playground app, and PostgreSQL |
| src/playground/.devcontainer.json | Devcontainer configuration for the playground manager itself |
| src/playground/caddy/Caddyfile | Caddy configuration with admin API enabled |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| type DockerService struct { | ||
| docker *DockerClient | ||
| db *DB | ||
| cancelFuncsLock sync.RWMutex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: no need for RWMutex. afaict, you are not using RLock.
| // Cancel previous build if one exists | ||
| if oldCancel, exists := s.cancelFuncs[appID]; exists { | ||
| log.Printf("Cancelling previous build for app %d", appID) | ||
| oldCancel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can get the func, clear the map. release the lock and do oldCancel outside the lock?
| } | ||
|
|
||
| // RemoveCancelFunc removes the cancel function after build completes or fails | ||
| func (s *DockerService) RemoveCancelFunc(appID int) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private?
| } | ||
|
|
||
| // GetPlaygroundLogs retrieves logs from the playground container | ||
| func (d *DockerClient) GetPlaygroundLogs(ctx context.Context, tail int) (string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not used
| var app App | ||
| var featuresStr string | ||
|
|
||
| err = db.conn.QueryRowContext(ctx, query, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if err :=
| output, err := inspectCmd.CombinedOutput() | ||
| if err != nil { | ||
| // Container doesn't exist | ||
| return "not_found", nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: const
| // If a build is already in progress for this app, it will be cancelled | ||
| func setupContainer(app *App, db *DB, dockerService *DockerService, caddyService *CaddyService) { | ||
| // Create context with 30 minute timeout | ||
| ctx, cancel := context.WithTimeout(context.Background(), DockerBuildTimeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we pass in the ctx for cancellation propagation
| @@ -0,0 +1,318 @@ | |||
| package main | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can we move db, caddy and docker to internal for readibility?
| hasDockerfile := strings.TrimSpace(req.Dockerfile) != "" | ||
| hasDockerImage := strings.TrimSpace(req.DockerImage) != "" | ||
|
|
||
| if !hasDockerfile && !hasDockerImage { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iirc, the ui you showed doesn't allow pass in dockerImage?
| // isValidAppName checks if app name contains only valid characters | ||
| func isValidAppName(name string) bool { | ||
| for _, char := range name { | ||
| if !((char >= 'a' && char <= 'z') || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unicode.IsLetter() and unicode.isDigit()
Summary
Introduces the Playground Apps Manager - a web-based system for creating, managing, and deploying custom devcontainer-based applications with real-time monitoring and build log streaming.
Overview
The Playground Apps Manager provides a self-service interface for users to create and manage containerized applications using devcontainer configurations. Apps are built with the devcontainer CLI and run as isolated containers with reverse-proxy routing through Caddy.
Key Features
App Management
Build System
linux/amd64for maximum compatibilityOptional Features
Built-in support for common feature sets:
wb): Java 17, AWS CLI, gcloud CLI, startup scripts (~4 min build)workbench-tools): Bioinformatics toolkit including bcftools, bedtools, plink, plink2, samtools, VEP, REGENIE (~8 min build)Features are automatically installed during devcontainer build with proper configuration.
Example Apps
Quick-start templates for common use cases:
Monitoring & Debugging
🤖 Generated with Claude Code