Skip to content

Conversation

@pantherman594
Copy link
Contributor

@pantherman594 pantherman594 commented Jan 13, 2026

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

  • Web UI: Full-featured interface for CRUD operations on apps
  • Dynamic Routing: Automatic Caddy configuration for reverse-proxy routing to each app
  • Container Lifecycle: Start, stop, and monitor container status
  • Real-time Status: Live updates for app and container states

Build System

  • Devcontainer-based: Uses devcontainer CLI for building and starting containers
  • Live Build Logs: Real-time streaming of build output to playground logs
  • Platform Support: Builds as linux/amd64 for maximum compatibility
  • Async Building: Apps build in background with status tracking

Optional Features

Built-in support for common feature sets:

  • Workbench CLI (wb): Java 17, AWS CLI, gcloud CLI, startup scripts (~4 min build)
  • Workbench Tools (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:

  • ttyd: Web-based terminal access
  • JupyterLab: Interactive notebook environment

Monitoring & Debugging

  • Container Logs: View logs for individual app containers
  • Playground Logs: System-level logs for build processes
  • Auto-refresh: Toggle auto-refresh (3s interval) for real-time log monitoring
  • Smart Scrolling: Maintains scroll position during log updates
image image image

🤖 Generated with Claude Code

@pantherman594 pantherman594 changed the title Playground app Add Playground Apps Manager - Web-based devcontainer app deployment system Jan 13, 2026
Copy link

Copilot AI left a 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
Copy link
Contributor

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()
Copy link
Contributor

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) {
Copy link
Contributor

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) {
Copy link
Contributor

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,
Copy link
Contributor

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
Copy link
Contributor

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)
Copy link
Contributor

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
Copy link
Contributor

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 {
Copy link
Contributor

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') ||
Copy link
Contributor

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()

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.

2 participants