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
62 changes: 62 additions & 0 deletions .deva.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# deva Configuration File Example
#
# This file demonstrates the .deva config file format.
# Config files are loaded in this order:
# 1. $XDG_CONFIG_HOME/deva/.deva (usually ~/.config/deva/.deva)
# 2. $HOME/.deva
# 3. ./.deva (project-specific)
# 4. ./.deva.local (gitignored overrides)
#
# Lines starting with # are comments.
# Blank lines are ignored.

# VOLUME Directives
# Mount host directories into container
# Format: VOLUME=<host-path>:<container-path>:<mode>
# Modes: ro (read-only), rw (read-write)
VOLUME=$HOME/.ssh:/home/deva/.ssh:ro
VOLUME=$HOME/projects/shared:/home/deva/shared:ro

# ENV Directives
# Set environment variables in container
# Format: ENV=<VAR_NAME>=<value>
ENV=EDITOR=vim
ENV=LANG=en_US.UTF-8

# Variable Assignments
# Set deva.sh behavior variables
# These control wrapper behavior, not container environment
AUTH_METHOD=claude
PROFILE=rust
EPHEMERAL=false

# Common Use Cases:
#
# 1. Mount read-only SSH keys:
# VOLUME=$HOME/.ssh:/home/deva/.ssh:ro
#
# 2. Mount project dependencies:
# VOLUME=$HOME/shared-libs:/home/deva/libs:ro
#
# 3. Set default editor:
# ENV=EDITOR=nvim
#
# 4. Enable Docker-in-Docker:
# ENV=DOCKER_IN_DOCKER=true
#
# 5. Set default profile:
# PROFILE=rust
#
# 6. Disable ephemeral containers (persistent):
# EPHEMERAL=false

# Project-Specific Config (.deva in project root):
#
# VOLUME=./vendor:/home/deva/project/vendor:ro
# ENV=DATABASE_URL=postgres://localhost/dev
# PROFILE=rust

# Personal Overrides (.deva.local - add to .gitignore):
#
# VOLUME=$HOME/personal-keys:/home/deva/keys:ro
# ENV=API_KEY=secret-key-here
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ jobs:

- name: Test help output
run: |
./claude.sh --help
./deva.sh --help
./claude-yolo --help

- name: Test version output
run: |
./claude.sh --version
./deva.sh --version
./claude-yolo --version

- name: Check version consistency
Expand Down
67 changes: 60 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:

env:
REGISTRY: ghcr.io
IMAGE_NAME: thevibeworks/ccyolo
IMAGE_NAME: thevibeworks/deva

jobs:
build-and-push:
Expand Down Expand Up @@ -48,21 +48,69 @@ jobs:
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
- name: Build and push base image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

build-and-push-rust:
name: Build and Push Rust Profile Image
runs-on: ubuntu-latest
needs: build-and-push
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

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

- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata for rust profile
id: meta-rust
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=tag,suffix=-rust
type=raw,value=rust,enable={{is_default_branch}}

- name: Build and push rust image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.rust
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta-rust.outputs.tags }}
labels: ${{ steps.meta-rust.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}

release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: build-and-push
needs: [build-and-push, build-and-push-rust]
permissions:
contents: write
steps:
Expand All @@ -80,15 +128,15 @@ jobs:
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi

- name: Update version in claude.sh
- name: Update version in deva.sh
run: |
VERSION="${{ steps.version.outputs.version }}"
# Remove 'v' prefix if present
VERSION=${VERSION#v}
sed -i "s/^VERSION=.*/VERSION=\"$VERSION\"/" claude.sh
sed -i "s/^VERSION=.*/VERSION=\"$VERSION\"/" deva.sh
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add claude.sh
git add deva.sh
git commit -m "Update version to $VERSION" || echo "No changes to commit"

- name: Generate release notes
Expand All @@ -105,15 +153,20 @@ jobs:
else
echo "## Initial Release" > release_notes.md
echo "" >> release_notes.md
echo "First release of Claude Code YOLO - Docker wrapper for Claude CLI with safe YOLO mode." >> release_notes.md
echo "First release of deva - Multi-agent development environment for Claude Code, Codex, and other AI coding assistants." >> release_notes.md
fi

echo "" >> release_notes.md
echo "## Docker Images" >> release_notes.md
echo "" >> release_notes.md
echo "**Base Profile (Python, Node, Go):**" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}\`" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:latest\`" >> release_notes.md
echo "" >> release_notes.md
echo "**Rust Profile (includes Rust toolchain):**" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}-rust\`" >> release_notes.md
echo "- \`ghcr.io/${{ env.IMAGE_NAME }}:rust\`" >> release_notes.md
echo "" >> release_notes.md
echo "## Supported Architectures" >> release_notes.md
echo "" >> release_notes.md
echo "- linux/amd64" >> release_notes.md
Expand Down
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,34 @@ All notable changes to Claude Code YOLO will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [0.9.1] - 2026-01-09

### Fixed
- **CRITICAL**: docker-entrypoint.sh UID/GID remapping broken since commit 5807889 (2025-12-29)
- Fixed selective `find -maxdepth 1 -user root` approach that skipped `.npm-global`, `.local`, etc.
- Implemented explicit whitelist approach for container-managed directories
- Prevents "env: 'claude': Permission denied" errors on container startup
- See docs/UID-GID-HANDLING-RESEARCH.md for industry patterns analysis
- GitHub workflows updated for deva rebrand
- release.yml: Fixed IMAGE_NAME from `ccyolo` to `deva`
- release.yml: Updated to modify `deva.sh` instead of `claude.sh`
- release.yml: Added rust profile build and push
- ci.yml: Updated tests to use `deva.sh` instead of `claude.sh`
- scripts/version-check.sh: Updated to check `deva.sh` version
- install.sh: Updated branding to "deva Multi-Agent Environment"

### Added
- Comprehensive UID/GID handling research document (docs/UID-GID-HANDLING-RESEARCH.md)
- Industry patterns from VS Code DevContainers, Jupyter, fixuid, and production best practices
- Comparison matrix of 6 different UID/GID handling approaches
- Validation that runtime UID fixing is legitimate for dev containers
- Developer log documenting the UID/GID fix investigation (docs/devlog/20260108-docker-uid-permission-fix.org)
- docker-entrypoint.sh: Improved execution order (setup_nonroot_user before ensure_agent_binaries)
- `.deva.example` - Reference config file demonstrating all supported directives (VOLUME, ENV, PROFILE, etc.)

### Changed
- GitHub release workflow now builds both base and rust profile images
- Release notes now document both image profiles (base and rust)

## [0.9.0] - 2026-01-08

Expand Down
2 changes: 1 addition & 1 deletion deva.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if [ -n "${DEVA_DOCKER_TAG+x}" ]; then
DEVA_DOCKER_TAG_ENV_SET=true
fi

VERSION="0.9.0"
VERSION="0.9.1"
DEVA_DOCKER_IMAGE="${DEVA_DOCKER_IMAGE:-ghcr.io/thevibeworks/deva}"
DEVA_DOCKER_TAG="${DEVA_DOCKER_TAG:-latest}"
DEVA_CONTAINER_PREFIX="${DEVA_CONTAINER_PREFIX:-deva}"
Expand Down
15 changes: 12 additions & 3 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,17 @@ setup_nonroot_user() {
DEVA_UID="$actual_uid"
fi
fi
# Only chown files owned by container, skip mounted volumes
find "$DEVA_HOME" -maxdepth 1 ! -type l -user root -exec chown "$DEVA_UID:$DEVA_GID" {} \; 2>/dev/null || true
# Fix container-managed directories (whitelist approach - safe for mounted volumes)
# These directories are created at image build time and must be chowned to match host UID
for dir in .npm-global .local .oh-my-zsh .skills .config .cache go; do
if [ -d "$DEVA_HOME/$dir" ] && [ ! -L "$DEVA_HOME/$dir" ]; then
chown -R "$DEVA_UID:$DEVA_GID" "$DEVA_HOME/$dir" 2>/dev/null || true
fi
done
# Fix container-created dotfiles
find "$DEVA_HOME" -maxdepth 1 \( -type f -o -type d \) -name '.*' \
! -name '..' ! -name '.' \
-exec chown "$DEVA_UID:$DEVA_GID" {} \; 2>/dev/null || true
fi

chmod 755 /root 2>/dev/null || true
Expand Down Expand Up @@ -288,10 +297,10 @@ main() {
cd "$WORKDIR"
fi

ensure_agent_binaries
setup_nonroot_user
fix_rust_permissions
fix_docker_socket_permissions
ensure_agent_binaries

if [ $# -eq 0 ]; then
if [ "$DEVA_AGENT" = "codex" ]; then
Expand Down
Loading