Skip to content
Merged
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
52 changes: 52 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Dependencies (will be installed fresh)
node_modules/

# Build outputs
dist/
build/
*.tsbuildinfo

# Git
.git/
.gitignore

# IDE
.idea/
.vscode/
*.swp
*.swo

# Test artifacts
coverage/
.nyc_output/

# Development files
*.log
*.local
.env
.env.*
!.env.example

# Documentation build
docs/book/

# Mobile app (not needed for server)
mobile/

# Electron-specific (not needed for server)
*.dmg
*.exe
*.AppImage
*.snap
*.deb
*.rpm

# Storybook
storybook-static/

# Misc
.DS_Store
Thumbs.db
*.md
!README.md
LICENSE
67 changes: 67 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,73 @@ jobs:
env:
ELECTRON_DISABLE_SANDBOX: 1

docker-smoke-test:
name: Docker Smoke Test
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
# Only run in merge queue (Docker builds are slow)
if: github.event_name == 'merge_group'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for git describe to find tags

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: .
load: true
tags: mux-server:test
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Start container
run: |
docker run -d --name mux-test -p 3000:3000 mux-server:test
# Wait for server to be ready
for i in {1..30}; do
if curl -sf http://localhost:3000/health; then
echo "Server is ready"
break
fi
echo "Waiting for server... ($i/30)"
sleep 1
done

- name: Health check
run: |
response=$(curl -sf http://localhost:3000/health)
echo "Health response: $response"
if ! echo "$response" | grep -q '"status":"ok"'; then
echo "Health check failed"
exit 1
fi

- name: Version check
run: |
response=$(curl -sf http://localhost:3000/version)
echo "Version response: $response"
# Verify response contains expected fields
if ! echo "$response" | grep -q '"mode":"server"'; then
echo "Version check failed: missing mode=server"
exit 1
fi
if ! echo "$response" | grep -q '"version"'; then
echo "Version check failed: missing version field"
exit 1
fi

- name: Show container logs on failure
if: failure()
run: docker logs mux-test

- name: Cleanup
if: always()
run: docker rm -f mux-test || true

check-codex-comments:
name: Check Codex Comments
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }}
Expand Down
103 changes: 103 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# mux server Docker image
# Multi-stage build for minimal runtime image size
#
# Build: docker build -t mux-server .
# Run: docker run -p 3000:3000 -v ~/.mux:/root/.mux mux-server
#
# See docker-compose.yml for easier orchestration

# ==============================================================================
# Stage 1: Build
# ==============================================================================
FROM node:22-slim AS builder

WORKDIR /app

# Install bun (used for package management and build tooling)
RUN npm install -g bun@1.2

# Install git (needed for version generation)
RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*

# Copy package files first for better layer caching
COPY package.json bun.lock bunfig.toml ./

# Copy postinstall script (needed by bun install)
COPY scripts/postinstall.sh scripts/

# Install build tools needed for native modules
RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*

# Install dependencies (postinstall detects server mode and skips Electron rebuild)
# Note: node-pty is in optionalDependencies and will be built for Node.js
RUN bun install --frozen-lockfile

# Copy source files needed for build
COPY src/ src/
COPY tsconfig.json tsconfig.main.json ./
COPY scripts/generate-version.sh scripts/
COPY index.html terminal.html vite.config.ts ./
COPY public/ public/
COPY static/ static/

# Remove test files (they import from tests/ which is outside rootDir)
RUN find src -name '*.test.ts' -delete

# Initialize git for version script (uses placeholder if not a real repo)
RUN git init && \
git config user.email "docker@build" && \
git config user.name "Docker Build" && \
git add -A && \
git commit -m "docker build" --allow-empty || true

# Generate version info
RUN ./scripts/generate-version.sh

# Build main process (server + backend)
RUN NODE_ENV=production bun x tsc -p tsconfig.main.json && \
NODE_ENV=production bun x tsc-alias -p tsconfig.main.json

# Build renderer (frontend)
RUN bun x vite build

# Copy static assets
RUN mkdir -p dist/static && cp -r static/* dist/static/ 2>/dev/null || true

# ==============================================================================
# Stage 2: Runtime
# ==============================================================================
FROM node:22-slim

WORKDIR /app

# Install runtime dependencies
# - git: required for workspace operations (clone, worktree, etc.)
# - openssh-client: required for SSH runtime support
RUN apt-get update && \
apt-get install -y git openssh-client && \
rm -rf /var/lib/apt/lists/*

# Copy built artifacts from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

# Create mux data directory
RUN mkdir -p /root/.mux

# Default environment variables
ENV NODE_ENV=production
ENV MUX_HOME=/root/.mux

# Expose server port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "fetch('http://localhost:3000/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))"

# Run mux server
# --host 0.0.0.0: bind to all interfaces (required for Docker networking)
# --port 3000: default port (can be remapped via docker run -p)
ENTRYPOINT ["node", "dist/cli/index.js", "server"]
CMD ["--host", "0.0.0.0", "--port", "3000"]
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# mux server docker-compose configuration
#
# Quick start:
# docker compose up -d
#
# Add a project:
# docker compose run --rm mux-server --add-project /projects/my-repo

services:
mux-server:
build: .
container_name: mux-server
ports:
- "3000:3000"
volumes:
- mux-data:/root/.mux
# Mount project directories as needed:
# - ~/projects:/projects:rw
restart: unless-stopped

volumes:
mux-data: