Compare commits
3 commits
fc2e5b1bca
...
a56da84b7a
| Author | SHA1 | Date | |
|---|---|---|---|
| a56da84b7a | |||
| 8171760807 | |||
| c387a7a365 |
5 changed files with 65 additions and 11 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
container-home/*
|
|
||||||
!container-home/.gitkeep
|
|
||||||
12
README.md
12
README.md
|
|
@ -9,7 +9,7 @@ the host.
|
||||||
- Arch Linux–based image
|
- Arch Linux–based image
|
||||||
- Runs as the host user (same username, UID, GID)
|
- Runs as the host user (same username, UID, GID)
|
||||||
- Mounts only the current project directory (same absolute path inside container)
|
- Mounts only the current project directory (same absolute path inside container)
|
||||||
- Persists OpenCode state in ./container-home directory
|
- Persists OpenCode state in XDG_DATA_HOME/opencode-container/container-home directory
|
||||||
- No access to SSH keys, passwords, or full `$HOME`
|
- No access to SSH keys, passwords, or full `$HOME`
|
||||||
- Simple shell function (`opencode`) to launch interactively
|
- Simple shell function (`opencode`) to launch interactively
|
||||||
|
|
||||||
|
|
@ -24,10 +24,14 @@ git clone https://git.jeena.net/jeena/opencode-container.git
|
||||||
Source the helper file `opencode.aliases` in your shell configuration
|
Source the helper file `opencode.aliases` in your shell configuration
|
||||||
(`.bashrc` or `.zshrc`) so the `opencode` function is available in new sessions.
|
(`.bashrc` or `.zshrc`) so the `opencode` function is available in new sessions.
|
||||||
|
|
||||||
We set up the ./container-home directory as a central $HOME inside of the
|
We set up the XDG_DATA_HOME/opencode-container/container-home directory as a central $HOME inside of the
|
||||||
container, independent of the session or project directory we start in. This
|
container, independent of the session or project directory we start in. This
|
||||||
presists the whole $HOME from inside the container so everything opencode
|
persists the whole $HOME from inside the container so everything OpenCode
|
||||||
writes into config files, etc. presists there.
|
writes into config files, etc. persists there.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
- `XDG_DATA_HOME`: Override default data directory (default: ~/.local/share)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
||||||
22
force-cleanup.sh
Executable file
22
force-cleanup.sh
Executable file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# opencode-container-cleanup.sh - Complete cleanup script for OpenCode container
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Stop and remove all opencode containers
|
||||||
|
echo "Stopping and removing opencode containers..."
|
||||||
|
docker ps -q --filter "name=oc-" | xargs -r docker stop
|
||||||
|
docker ps -a -q --filter "name=oc-" | xargs -r docker rm -f
|
||||||
|
|
||||||
|
# Remove the Docker image
|
||||||
|
echo "Removing opencode-container image..."
|
||||||
|
docker rmi opencode-container:latest 2>/dev/null || true
|
||||||
|
|
||||||
|
# Remove entire opencode-container directory
|
||||||
|
CONTAINER_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/opencode-container"
|
||||||
|
if [ -d "$CONTAINER_DIR" ]; then
|
||||||
|
echo "Removing ~/.local/share/opencode-container directory..."
|
||||||
|
rm -rf "$CONTAINER_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Complete cleanup finished!"
|
||||||
|
|
@ -16,14 +16,43 @@ class OpenCodeContainer:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.project_path = Path.cwd().resolve()
|
self.project_path = Path.cwd().resolve()
|
||||||
self.project_id = self._project_id(self.project_path)
|
self.project_id = self._project_id(self.project_path)
|
||||||
|
|
||||||
|
# Store host user info once
|
||||||
|
self.host_username = os.environ.get('USER', 'dev')
|
||||||
|
self.host_uid = os.getuid()
|
||||||
|
self.host_gid = os.getgid()
|
||||||
|
|
||||||
self.container_name = f"oc-{self.project_path.name}-{self.project_id}"
|
self.container_name = f"oc-{self.project_path.name}-{self.project_id}"
|
||||||
self.docker_context_dir = Path(__file__).resolve().parent
|
self.docker_context_dir = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
def _get_xdg_data_home(self) -> Path:
|
||||||
|
"""Get XDG_DATA_HOME with fallback to ~/.local/share"""
|
||||||
|
xdg_data = os.environ.get('XDG_DATA_HOME')
|
||||||
|
if xdg_data:
|
||||||
|
return Path(xdg_data)
|
||||||
|
return Path.home() / '.local' / 'share'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def container_home_path(self) -> Path:
|
||||||
|
"""Container home directory in XDG_DATA_HOME"""
|
||||||
|
return self._get_xdg_data_home() / 'opencode-container' / 'container-home'
|
||||||
|
|
||||||
# =========================
|
# =========================
|
||||||
# Public entrypoint
|
# Public entrypoint
|
||||||
# =========================
|
# =========================
|
||||||
|
|
||||||
def run(self, args: list[str]) -> None:
|
def run(self, args: list[str]) -> None:
|
||||||
|
# Ensure container home directory exists
|
||||||
|
self.container_home_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Pre-create project directory structure to prevent root-owned directories
|
||||||
|
try:
|
||||||
|
relative_path = self.project_path.relative_to(Path.home())
|
||||||
|
(self.container_home_path / relative_path).mkdir(parents=True, exist_ok=True)
|
||||||
|
except ValueError:
|
||||||
|
# Project is outside home directory - no action needed
|
||||||
|
pass
|
||||||
|
|
||||||
if not self.image_exists():
|
if not self.image_exists():
|
||||||
self.build_image()
|
self.build_image()
|
||||||
|
|
||||||
|
|
@ -54,9 +83,12 @@ class OpenCodeContainer:
|
||||||
).returncode == 0
|
).returncode == 0
|
||||||
|
|
||||||
def build_image(self) -> None:
|
def build_image(self) -> None:
|
||||||
print(f"Building image '{self.IMAGE}'")
|
print(f"Building image '{self.IMAGE}' with user {self.host_username} ({self.host_uid}:{self.host_gid})")
|
||||||
self._run([
|
self._run([
|
||||||
"docker", "build",
|
"docker", "build",
|
||||||
|
"--build-arg", f"USERNAME={self.host_username}",
|
||||||
|
"--build-arg", f"UID={self.host_uid}",
|
||||||
|
"--build-arg", f"GID={self.host_gid}",
|
||||||
"-t", self.IMAGE,
|
"-t", self.IMAGE,
|
||||||
str(self.docker_context_dir),
|
str(self.docker_context_dir),
|
||||||
])
|
])
|
||||||
|
|
@ -66,16 +98,14 @@ class OpenCodeContainer:
|
||||||
# =========================
|
# =========================
|
||||||
|
|
||||||
def create_container(self) -> None:
|
def create_container(self) -> None:
|
||||||
uid = os.getuid()
|
|
||||||
gid = os.getgid()
|
|
||||||
|
|
||||||
print(f"Creating container '{self.container_name}'")
|
print(f"Creating container '{self.container_name}'")
|
||||||
|
|
||||||
self._run([
|
self._run([
|
||||||
"docker", "create",
|
"docker", "create",
|
||||||
"--name", self.container_name,
|
"--name", self.container_name,
|
||||||
"--user", f"{uid}:{gid}",
|
"--user", f"{self.host_uid}:{self.host_gid}",
|
||||||
"--volume", f"{self.project_path}:{self.project_path}",
|
"--volume", f"{self.project_path}:{self.project_path}",
|
||||||
|
"--volume", f"{self.container_home_path}:/home/{self.host_username}",
|
||||||
self.IMAGE,
|
self.IMAGE,
|
||||||
"sh", "-c", "sleep infinity",
|
"sh", "-c", "sleep infinity",
|
||||||
])
|
])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue