Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
92 changes: 0 additions & 92 deletions .env.example

This file was deleted.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ secrets.yaml
.env
.env.local
.env.*.local
# Note: .env.example is NOT ignored (it's a template)
# Note: .env may be generated at runtime by egg --compose

# IDE
.idea/
Expand Down
148 changes: 96 additions & 52 deletions bin/egg-deploy
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#
# Prerequisites:
# - Docker and Docker Compose installed
# - .env file configured (copy from .env.example)
# - ~/.config/egg/config.yaml configured (run egg-deploy init)
# - repositories.yaml in EGG_CONFIG_DIR

set -e
Expand Down Expand Up @@ -56,19 +56,23 @@ check_prerequisites() {
fi
}

# Load environment file
load_env() {
local env_file="$PROJECT_DIR/.env"
# Load configuration from ~/.config/egg/config.yaml via Python helper
load_config() {
local config_dir="${EGG_CONFIG_DIR:-$HOME/.config/egg}"
local helper="$PROJECT_DIR/shared/egg_config/compose_config.py"

if [ ! -f "$env_file" ]; then
warn ".env file not found. Run 'egg-deploy init' to create one."
if [ ! -f "$helper" ]; then
error "compose_config.py helper not found at $helper"
return 1
fi

set -a
# shellcheck source=/dev/null
source "$env_file"
set +a
if [ ! -f "$config_dir/config.yaml" ]; then
warn "config.yaml not found in $config_dir. Run 'egg-deploy init' to create one."
return 1
fi

# Read config.yaml + secrets and export as env vars
eval "$(python3 "$helper" "$config_dir")"
}

# Validate configuration
Expand Down Expand Up @@ -160,48 +164,87 @@ show_status() {
init_config() {
info "Initializing egg configuration..."

local env_file="$PROJECT_DIR/.env"

# Create .env from example if it doesn't exist
if [ ! -f "$env_file" ]; then
if [ -f "$PROJECT_DIR/.env.example" ]; then
cp "$PROJECT_DIR/.env.example" "$env_file"
info "Created .env from .env.example"
else
error ".env.example not found"
exit 1
fi
local config_dir="${EGG_CONFIG_DIR:-$HOME/.config/egg}"
local config_file="$config_dir/config.yaml"
local secrets_file="$config_dir/secrets.env"

# Create config directory
mkdir -p "$config_dir"

# Create config.yaml if it doesn't exist
if [ ! -f "$config_file" ]; then
local host_uid
host_uid=$(id -u)
local host_gid
host_gid=$(id -g)

cat > "$config_file" << YAML_EOF
# Egg Configuration
# Generated by egg-deploy init

# Host identity (required for worktree bind mounts)
host_home: $HOME
host_uid: $host_uid
host_gid: $host_gid

# Git identity for commits
git_name: egg
git_email: egg@localhost

# Docker Compose project name (prefixes container/network names)
compose_project_name: egg

# Network ports
gateway_api_port: 9848
gateway_proxy_port: 3129
orchestrator_api_port: 9849

# MCP server (always enabled, configure port and rate limit)
mcp_server_port: 9850
mcp_rate_limit: 30

# Anthropic authentication method
anthropic_auth_method: oauth

# Image overrides (uncomment to use pre-built images)
# gateway_image: ghcr.io/jwbron/egg-gateway:latest
# orchestrator_image: ghcr.io/jwbron/egg-orchestrator:latest
# sandbox_image: ghcr.io/jwbron/egg-sandbox:latest

# Bot mode (uncomment to enable GitHub App authentication)
# gateway_bot_name: your-bot-name
# gateway_bot_branch_prefix: your-bot-name
# gateway_trusted_users: user1,user2
YAML_EOF
info "Created config.yaml at $config_file"
else
warn ".env already exists, not overwriting"
warn "config.yaml already exists, not overwriting"
fi

# Generate launcher secret if not set
if ! grep -q "^EGG_LAUNCHER_SECRET=." "$env_file" 2>/dev/null; then
local secret
secret=$(openssl rand -hex 32)
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/^EGG_LAUNCHER_SECRET=.*/EGG_LAUNCHER_SECRET=$secret/" "$env_file"
else
sed -i "s/^EGG_LAUNCHER_SECRET=.*/EGG_LAUNCHER_SECRET=$secret/" "$env_file"
fi
# Generate launcher secret if not present
if [ ! -f "$config_dir/launcher-secret" ]; then
openssl rand -hex 32 > "$config_dir/launcher-secret"
chmod 600 "$config_dir/launcher-secret"
success "Generated launcher secret"
fi

# Set HOST_UID and HOST_GID
local host_uid
host_uid=$(id -u)
local host_gid
host_gid=$(id -g)
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' "s/^HOST_UID=.*/HOST_UID=$host_uid/" "$env_file"
sed -i '' "s/^HOST_GID=.*/HOST_GID=$host_gid/" "$env_file"
sed -i '' "s|^HOST_HOME=.*|HOST_HOME=$HOME|" "$env_file"
else
sed -i "s/^HOST_UID=.*/HOST_UID=$host_uid/" "$env_file"
sed -i "s/^HOST_GID=.*/HOST_GID=$host_gid/" "$env_file"
sed -i "s|^HOST_HOME=.*|HOST_HOME=$HOME|" "$env_file"
# Create secrets.env template if it doesn't exist
if [ ! -f "$secrets_file" ]; then
cat > "$secrets_file" << 'SECRETS_EOF'
# Egg Secrets
# Generated by egg-deploy init
# chmod 600 this file

# GitHub personal access token (for git operations)
# Required scopes: repo, read:org (if accessing org repos)
GITHUB_USER_TOKEN=

# Bot GitHub token (if using GitHub App instead of PAT)
# BOT_GITHUB_TOKEN=
SECRETS_EOF
chmod 600 "$secrets_file"
info "Created secrets.env at $secrets_file"
fi
info "Set HOST_UID=$host_uid, HOST_GID=$host_gid, HOST_HOME=$HOME"

# Create repos directory
local repos_dir="$PROJECT_DIR/repos"
Expand All @@ -214,15 +257,16 @@ init_config() {
success "Configuration initialized!"
echo ""
echo "Next steps:"
echo " 1. Edit $env_file and set GITHUB_USER_TOKEN"
echo " 2. Create repositories.yaml (see egg.yaml.example for format)"
echo " 1. Set your GitHub token: echo 'YOUR_TOKEN' > $config_dir/github-token && chmod 600 $config_dir/github-token"
echo " Or edit $secrets_file and set GITHUB_USER_TOKEN"
echo " 2. Create repositories.yaml (see config/repositories.yaml.example)"
echo " 3. Run: egg-deploy up"
}

# Command handlers
cmd_up() {
check_prerequisites
if ! load_env; then
if ! load_config; then
error "Run 'egg-deploy init' first to create configuration"
exit 1
fi
Expand Down Expand Up @@ -250,7 +294,7 @@ cmd_up() {

cmd_down() {
check_prerequisites
load_env 2>/dev/null || true
load_config 2>/dev/null || true

info "Stopping egg containers..."
docker compose -f "$COMPOSE_FILE" down --remove-orphans
Expand All @@ -260,21 +304,21 @@ cmd_down() {

cmd_status() {
check_prerequisites
load_env 2>/dev/null || true
load_config 2>/dev/null || true

show_status
}

cmd_logs() {
check_prerequisites
load_env 2>/dev/null || true
load_config 2>/dev/null || true

docker compose -f "$COMPOSE_FILE" logs -f gateway
}

cmd_build() {
check_prerequisites
load_env 2>/dev/null || true
load_config 2>/dev/null || true

info "Building egg images..."
docker compose -f "$COMPOSE_FILE" build
Expand Down
36 changes: 35 additions & 1 deletion config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ All persistent user configuration is consolidated under `~/.config/egg/`:

```
~/.config/egg/
├── config.yaml # Non-secret settings (Slack channel, sync intervals, etc.)
├── config.yaml # All non-secret settings (compose, ports, identity, etc.)
├── secrets.env # All secrets (Slack, GitHub, Confluence, JIRA tokens)
├── launcher-secret # Gateway launcher secret (dedicated file)
├── github-token # GitHub token (dedicated file)
├── github-app-id # GitHub App ID (if using App auth)
├── github-app-installation-id # GitHub App Installation ID
Expand Down Expand Up @@ -58,7 +59,40 @@ This directory respects `$XDG_CACHE_HOME` if set.

**Note**: Previously this was `~/.egg/`. The egg script auto-migrates on first run.

## config.yaml Schema

The `config.yaml` file stores all non-secret settings for egg, including Docker Compose
configuration (previously in `.env`). See `config/config.yaml.example` for a full template.

| Key | Env Var | Default | Description |
|-----|---------|---------|-------------|
| `host_home` | `HOST_HOME` | — | Host home directory (required for worktree mounts) |
| `host_uid` | `HOST_UID` | `1000` | Host UID for file ownership |
| `host_gid` | `HOST_GID` | `1000` | Host GID for file ownership |
| `git_name` | `EGG_USER_GIT_NAME` | `egg` | Git user name for commits |
| `git_email` | `EGG_USER_GIT_EMAIL` | `egg@localhost` | Git email for commits |
| `compose_project_name` | `COMPOSE_PROJECT_NAME` | `egg` | Docker Compose project name |
| `gateway_api_port` | `GATEWAY_API_PORT` | `9848` | Gateway API port |
| `gateway_proxy_port` | `GATEWAY_PROXY_PORT` | `3129` | Squid proxy port |
| `orchestrator_api_port` | `ORCHESTRATOR_API_PORT` | `9849` | Orchestrator API port |
| `mcp_server_port` | `EGG_MCP_SERVER_PORT` | `9850` | MCP server port |
| `mcp_rate_limit` | `EGG_MCP_RATE_LIMIT` | `30` | MCP requests per minute |
| `anthropic_auth_method` | — | `oauth` | Auth method: `oauth` or `api_key` |
| `gateway_image` | `EGG_GATEWAY_IMAGE` | — | Gateway image override |
| `orchestrator_image` | `EGG_ORCHESTRATOR_IMAGE` | — | Orchestrator image override |
| `sandbox_image` | `EGG_SANDBOX_IMAGE` | — | Sandbox image override |
| `gateway_bot_name` | `GATEWAY_BOT_NAME` | — | Bot name (GitHub App mode) |
| `gateway_bot_branch_prefix` | `GATEWAY_BOT_BRANCH_PREFIX` | — | Bot branch prefix |
| `gateway_trusted_users` | `GATEWAY_TRUSTED_USERS` | — | Trusted user list |

Secrets (`EGG_LAUNCHER_SECRET`, `GITHUB_USER_TOKEN`, `BOT_GITHUB_TOKEN`) are stored in
`secrets.env` or dedicated files (`launcher-secret`, `github-token`), never in `config.yaml`.

The `egg-deploy` script reads `config.yaml` via `shared/egg_config/compose_config.py`
and exports the values as environment variables for `docker-compose.yml`.

**Templates:**
- `config/config.yaml.example` - Full config.yaml template with all settings
- `config/secrets.template.env` - Secrets template

**In-repo modules:**
Expand Down
Loading
Loading