@opencode-cloud/core 0.1.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,444 @@
1
+ # =============================================================================
2
+ # opencode-cloud Container Image
3
+ # =============================================================================
4
+ # A comprehensive development environment for AI-assisted coding with opencode.
5
+ #
6
+ # Features:
7
+ # - Ubuntu 24.04 LTS (noble) base
8
+ # - Non-root user with passwordless sudo
9
+ # - Multiple languages via mise (Node.js, Python, Rust, Go)
10
+ # - Modern CLI tools (ripgrep, eza, fzf, lazygit, etc.)
11
+ # - GSD opencode plugin for task management
12
+ #
13
+ # Usage:
14
+ # docker build -t opencode-cloud .
15
+ # docker run -it opencode-cloud
16
+ #
17
+ # =============================================================================
18
+
19
+ # -----------------------------------------------------------------------------
20
+ # Stage 1: Builder
21
+ # -----------------------------------------------------------------------------
22
+ # Install build dependencies and tools that require compilation
23
+ FROM ubuntu:24.04 AS builder
24
+
25
+ # Avoid interactive prompts during package installation
26
+ ENV DEBIAN_FRONTEND=noninteractive
27
+ ENV TZ=UTC
28
+
29
+ # Install build essentials
30
+ RUN apt-get update && apt-get install -y --no-install-recommends \
31
+ build-essential \
32
+ ca-certificates \
33
+ curl \
34
+ git \
35
+ && rm -rf /var/lib/apt/lists/*
36
+
37
+ # -----------------------------------------------------------------------------
38
+ # Stage 2: Runtime
39
+ # -----------------------------------------------------------------------------
40
+ FROM ubuntu:24.04 AS runtime
41
+
42
+ # OCI Labels for image metadata
43
+ LABEL org.opencontainers.image.title="opencode-cloud"
44
+ LABEL org.opencontainers.image.description="AI-assisted development environment with opencode"
45
+ LABEL org.opencontainers.image.url="https://github.com/pRizz/opencode-cloud"
46
+ LABEL org.opencontainers.image.source="https://github.com/pRizz/opencode-cloud"
47
+ LABEL org.opencontainers.image.vendor="pRizz"
48
+ LABEL org.opencontainers.image.licenses="MIT"
49
+ LABEL org.opencontainers.image.base.name="ubuntu:24.04"
50
+
51
+ # Environment configuration
52
+ ENV DEBIAN_FRONTEND=noninteractive
53
+ ENV TZ=UTC
54
+ ENV LANG=C.UTF-8
55
+ ENV LC_ALL=C.UTF-8
56
+
57
+ # -----------------------------------------------------------------------------
58
+ # System Dependencies
59
+ # -----------------------------------------------------------------------------
60
+ # Install core system packages in logical groups for better caching
61
+
62
+ # Group 1: Core utilities and build tools
63
+ RUN apt-get update && apt-get install -y --no-install-recommends \
64
+ # Signal handling
65
+ tini \
66
+ dumb-init \
67
+ # Shell and terminal
68
+ zsh \
69
+ tmux \
70
+ # Editors
71
+ vim \
72
+ neovim \
73
+ nano \
74
+ # Build essentials
75
+ build-essential \
76
+ pkg-config \
77
+ cmake \
78
+ # Version control
79
+ git \
80
+ git-lfs \
81
+ # Core utilities
82
+ curl \
83
+ wget \
84
+ ca-certificates \
85
+ gnupg \
86
+ lsb-release \
87
+ software-properties-common \
88
+ sudo \
89
+ openssh-client \
90
+ # Process/system tools
91
+ htop \
92
+ procps \
93
+ less \
94
+ file \
95
+ tree \
96
+ # JSON/YAML processing
97
+ jq \
98
+ # Network tools
99
+ netcat-openbsd \
100
+ iputils-ping \
101
+ dnsutils \
102
+ # Compression
103
+ zip \
104
+ unzip \
105
+ xz-utils \
106
+ p7zip-full \
107
+ && rm -rf /var/lib/apt/lists/*
108
+
109
+ # Group 2: Database clients
110
+ RUN apt-get update && apt-get install -y --no-install-recommends \
111
+ sqlite3 \
112
+ postgresql-client \
113
+ default-mysql-client \
114
+ && rm -rf /var/lib/apt/lists/*
115
+
116
+ # Group 3: Development libraries (for compiling tools)
117
+ RUN apt-get update && apt-get install -y --no-install-recommends \
118
+ libssl-dev \
119
+ libffi-dev \
120
+ zlib1g-dev \
121
+ libbz2-dev \
122
+ libreadline-dev \
123
+ libsqlite3-dev \
124
+ libncurses-dev \
125
+ liblzma-dev \
126
+ && rm -rf /var/lib/apt/lists/*
127
+
128
+ # -----------------------------------------------------------------------------
129
+ # Create Non-Root User
130
+ # -----------------------------------------------------------------------------
131
+ # Create 'opencode' user with passwordless sudo
132
+ RUN useradd -m -s /bin/zsh -G sudo opencode \
133
+ && echo "opencode ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/opencode \
134
+ && chmod 0440 /etc/sudoers.d/opencode
135
+
136
+ # Switch to opencode user for remaining setup
137
+ USER opencode
138
+ WORKDIR /home/opencode
139
+
140
+ # Set up directories
141
+ RUN mkdir -p \
142
+ /home/opencode/.config \
143
+ /home/opencode/.local/bin \
144
+ /home/opencode/.local/share \
145
+ /home/opencode/.cache \
146
+ /home/opencode/workspace
147
+
148
+ # Add local bin to PATH
149
+ ENV PATH="/home/opencode/.local/bin:${PATH}"
150
+
151
+ # -----------------------------------------------------------------------------
152
+ # Shell Setup: Zsh + Oh My Zsh + Starship
153
+ # -----------------------------------------------------------------------------
154
+ # Install Oh My Zsh
155
+ RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
156
+
157
+ # Install Starship prompt
158
+ RUN curl -sS https://starship.rs/install.sh | sh -s -- --yes --bin-dir /home/opencode/.local/bin
159
+
160
+ # Configure zsh with starship
161
+ RUN echo 'eval "$(starship init zsh)"' >> /home/opencode/.zshrc \
162
+ && echo 'export PATH="/home/opencode/.local/bin:$PATH"' >> /home/opencode/.zshrc
163
+
164
+ # -----------------------------------------------------------------------------
165
+ # mise: Universal Version Manager
166
+ # -----------------------------------------------------------------------------
167
+ # Install mise for managing Node.js, Python, Rust, Go
168
+ RUN curl https://mise.run | sh \
169
+ && echo 'eval "$(/home/opencode/.local/bin/mise activate zsh)"' >> /home/opencode/.zshrc
170
+
171
+ # Install language runtimes via mise
172
+ # Using specific LTS/stable versions for reproducibility
173
+ RUN /home/opencode/.local/bin/mise install node@lts \
174
+ && /home/opencode/.local/bin/mise install python@3.12 \
175
+ && /home/opencode/.local/bin/mise install go@latest \
176
+ && /home/opencode/.local/bin/mise use --global node@lts \
177
+ && /home/opencode/.local/bin/mise use --global python@3.12 \
178
+ && /home/opencode/.local/bin/mise use --global go@latest
179
+
180
+ # Set up mise shims in PATH for non-interactive shells
181
+ ENV PATH="/home/opencode/.local/share/mise/shims:${PATH}"
182
+
183
+ # -----------------------------------------------------------------------------
184
+ # Rust Installation
185
+ # -----------------------------------------------------------------------------
186
+ # Install Rust via rustup (mise rust support is experimental)
187
+ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable \
188
+ && . /home/opencode/.cargo/env \
189
+ && rustup component add rust-analyzer rustfmt clippy
190
+
191
+ ENV PATH="/home/opencode/.cargo/bin:${PATH}"
192
+
193
+ # -----------------------------------------------------------------------------
194
+ # Package Managers
195
+ # -----------------------------------------------------------------------------
196
+ # Switch to bash for mise activation (mise outputs bash-specific syntax)
197
+ SHELL ["/bin/bash", "-c"]
198
+
199
+ # Install pnpm (corepack is included with Node.js)
200
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
201
+ && corepack enable \
202
+ && corepack prepare pnpm@latest --activate
203
+
204
+ # Set up pnpm global bin directory
205
+ ENV PNPM_HOME="/home/opencode/.local/share/pnpm"
206
+ ENV PATH="${PNPM_HOME}:${PATH}"
207
+ RUN mkdir -p "${PNPM_HOME}"
208
+
209
+ # Install uv (fast Python package manager)
210
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
211
+
212
+ # Install pipx for isolated Python application installs
213
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
214
+ && pip install --user pipx \
215
+ && pipx ensurepath
216
+
217
+ # Install global TypeScript compiler
218
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
219
+ && pnpm add -g typescript
220
+
221
+ # -----------------------------------------------------------------------------
222
+ # Modern CLI Tools (Rust-based)
223
+ # -----------------------------------------------------------------------------
224
+ # Install via cargo for latest versions
225
+ RUN . /home/opencode/.cargo/env \
226
+ && cargo install --locked \
227
+ ripgrep \
228
+ eza
229
+
230
+ # Install lazygit (Go-based)
231
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
232
+ && go install github.com/jesseduffield/lazygit@latest
233
+
234
+ # -----------------------------------------------------------------------------
235
+ # Additional Development Tools
236
+ # -----------------------------------------------------------------------------
237
+ # Install fzf
238
+ RUN git clone --depth 1 https://github.com/junegunn/fzf.git /home/opencode/.fzf \
239
+ && /home/opencode/.fzf/install --all --no-bash --no-fish
240
+
241
+ # Install yq (YAML processor)
242
+ RUN curl -sL https://github.com/mikefarah/yq/releases/latest/download/yq_linux_$(dpkg --print-architecture) -o /home/opencode/.local/bin/yq \
243
+ && chmod +x /home/opencode/.local/bin/yq
244
+
245
+ # Install direnv
246
+ USER root
247
+ RUN apt-get update && apt-get install -y --no-install-recommends direnv \
248
+ && rm -rf /var/lib/apt/lists/*
249
+ USER opencode
250
+ RUN echo 'eval "$(direnv hook zsh)"' >> /home/opencode/.zshrc
251
+
252
+ # Install HTTPie
253
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
254
+ && pipx install httpie
255
+
256
+ # Install shellcheck and shfmt
257
+ USER root
258
+ RUN apt-get update && apt-get install -y --no-install-recommends shellcheck \
259
+ && rm -rf /var/lib/apt/lists/*
260
+ USER opencode
261
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
262
+ && go install mvdan.cc/sh/v3/cmd/shfmt@latest
263
+
264
+ # Install btop (system monitor)
265
+ USER root
266
+ RUN apt-get update && apt-get install -y --no-install-recommends btop \
267
+ && rm -rf /var/lib/apt/lists/*
268
+ USER opencode
269
+
270
+ # -----------------------------------------------------------------------------
271
+ # GitHub CLI
272
+ # -----------------------------------------------------------------------------
273
+ USER root
274
+ RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
275
+ && chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
276
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list \
277
+ && apt-get update && apt-get install -y --no-install-recommends gh \
278
+ && rm -rf /var/lib/apt/lists/*
279
+ USER opencode
280
+
281
+ # -----------------------------------------------------------------------------
282
+ # CI/CD Tools
283
+ # -----------------------------------------------------------------------------
284
+ # Install act (run GitHub Actions locally)
285
+ RUN curl -sL https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash -s -- -b /home/opencode/.local/bin
286
+
287
+ # -----------------------------------------------------------------------------
288
+ # Rust Tooling
289
+ # -----------------------------------------------------------------------------
290
+ RUN . /home/opencode/.cargo/env \
291
+ && cargo install --locked \
292
+ cargo-nextest \
293
+ cargo-audit \
294
+ cargo-deny
295
+
296
+ # Install mold (fast linker) via apt for easier setup
297
+ USER root
298
+ RUN apt-get update && apt-get install -y --no-install-recommends mold \
299
+ && rm -rf /var/lib/apt/lists/*
300
+ USER opencode
301
+
302
+ # -----------------------------------------------------------------------------
303
+ # Code Quality Tools
304
+ # -----------------------------------------------------------------------------
305
+ # JavaScript/TypeScript tools
306
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
307
+ && pnpm add -g \
308
+ prettier \
309
+ eslint \
310
+ @biomejs/biome
311
+
312
+ # Python tools
313
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
314
+ && pipx install black \
315
+ && pipx install ruff
316
+
317
+ # Test runners (commonly needed)
318
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
319
+ && pnpm add -g jest vitest
320
+
321
+ # Python pytest via pipx
322
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
323
+ && pipx install pytest
324
+
325
+ # -----------------------------------------------------------------------------
326
+ # Protocol Buffers / gRPC
327
+ # -----------------------------------------------------------------------------
328
+ USER root
329
+ RUN apt-get update && apt-get install -y --no-install-recommends protobuf-compiler \
330
+ && rm -rf /var/lib/apt/lists/*
331
+ USER opencode
332
+
333
+ # Install grpcurl
334
+ RUN eval "$(/home/opencode/.local/bin/mise activate bash)" \
335
+ && go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
336
+
337
+ # -----------------------------------------------------------------------------
338
+ # opencode Installation
339
+ # -----------------------------------------------------------------------------
340
+ # Install opencode using official install script
341
+ # The script installs to ~/.opencode/bin/
342
+ RUN curl -fsSL https://opencode.ai/install | bash \
343
+ && ls -la /home/opencode/.opencode/bin/opencode \
344
+ && /home/opencode/.opencode/bin/opencode --version
345
+
346
+ # Add opencode to PATH
347
+ ENV PATH="/home/opencode/.opencode/bin:${PATH}"
348
+
349
+ # -----------------------------------------------------------------------------
350
+ # GSD Plugin Installation
351
+ # -----------------------------------------------------------------------------
352
+ # Install the GSD (Get Shit Done) plugin for opencode
353
+ RUN git clone https://github.com/rokicool/gsd-opencode.git /home/opencode/.config/opencode/plugins/gsd-opencode
354
+
355
+ # -----------------------------------------------------------------------------
356
+ # Sensible Defaults Configuration
357
+ # -----------------------------------------------------------------------------
358
+ # Git configuration
359
+ RUN git config --global init.defaultBranch main \
360
+ && git config --global core.editor "nvim" \
361
+ && git config --global pull.rebase false \
362
+ && git config --global merge.conflictstyle diff3 \
363
+ && git config --global diff.colorMoved default
364
+
365
+ # Starship configuration (minimal, fast prompt)
366
+ RUN mkdir -p /home/opencode/.config \
367
+ && cat > /home/opencode/.config/starship.toml << 'STARSHIP'
368
+ # Minimal starship config for fast prompt
369
+ format = """
370
+ $directory\
371
+ $git_branch\
372
+ $git_status\
373
+ $character"""
374
+
375
+ [directory]
376
+ truncation_length = 3
377
+ truncate_to_repo = true
378
+
379
+ [git_branch]
380
+ format = "[$branch]($style) "
381
+ style = "bold purple"
382
+
383
+ [git_status]
384
+ format = '([$all_status$ahead_behind]($style) )'
385
+
386
+ [character]
387
+ success_symbol = "[>](bold green)"
388
+ error_symbol = "[>](bold red)"
389
+ STARSHIP
390
+
391
+ # Shell aliases
392
+ RUN cat >> /home/opencode/.zshrc << 'ALIASES'
393
+
394
+ # Modern CLI aliases
395
+ alias ls="eza --icons"
396
+ alias ll="eza -l --icons"
397
+ alias la="eza -la --icons"
398
+ alias lt="eza --tree --icons"
399
+ alias grep="rg"
400
+ alias top="btop"
401
+
402
+ # Git aliases
403
+ alias g="git"
404
+ alias gs="git status"
405
+ alias gd="git diff"
406
+ alias gc="git commit"
407
+ alias gp="git push"
408
+ alias gl="git pull"
409
+ alias gco="git checkout"
410
+ alias gb="git branch"
411
+ alias lg="lazygit"
412
+
413
+ # Docker aliases (for Docker-in-Docker)
414
+ alias d="docker"
415
+ alias dc="docker compose"
416
+
417
+ ALIASES
418
+
419
+ # Set up pipx path
420
+ RUN echo 'export PATH="/home/opencode/.local/bin:$PATH"' >> /home/opencode/.zshrc
421
+
422
+ # -----------------------------------------------------------------------------
423
+ # Health Check
424
+ # -----------------------------------------------------------------------------
425
+ # Port 3000 must match OPENCODE_WEB_PORT in container.rs
426
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
427
+ CMD curl -f http://localhost:3000/health || exit 1
428
+
429
+ # -----------------------------------------------------------------------------
430
+ # Final Configuration
431
+ # -----------------------------------------------------------------------------
432
+ WORKDIR /home/opencode/workspace
433
+
434
+ # Expose opencode web port (matches OPENCODE_WEB_PORT in container.rs)
435
+ EXPOSE 3000
436
+
437
+ # Use tini as init system for proper signal handling
438
+ ENTRYPOINT ["/usr/bin/tini", "--"]
439
+
440
+ # Default command: start opencode web interface
441
+ # - Port 3000 must match OPENCODE_WEB_PORT in container.rs
442
+ # - Using full path to ensure binary is found (opencode installs to ~/.opencode/bin/)
443
+ # - --hostname 0.0.0.0 is required for Docker port mapping (default 127.0.0.1 only listens on loopback)
444
+ CMD ["/home/opencode/.opencode/bin/opencode", "web", "--port", "3000", "--hostname", "0.0.0.0"]
@@ -0,0 +1,84 @@
1
+ //! Docker client wrapper with connection handling
2
+ //!
3
+ //! This module provides a wrapped Docker client that handles connection
4
+ //! errors gracefully and provides clear error messages.
5
+
6
+ use bollard::Docker;
7
+
8
+ use super::error::DockerError;
9
+
10
+ /// Docker client wrapper with connection handling
11
+ pub struct DockerClient {
12
+ inner: Docker,
13
+ }
14
+
15
+ impl DockerClient {
16
+ /// Create new client connecting to local Docker daemon
17
+ ///
18
+ /// Uses platform-appropriate socket (Unix socket on Linux/macOS).
19
+ /// Returns a clear error if Docker is not running or accessible.
20
+ pub fn new() -> Result<Self, DockerError> {
21
+ let docker = Docker::connect_with_local_defaults()
22
+ .map_err(|e| DockerError::Connection(e.to_string()))?;
23
+
24
+ Ok(Self { inner: docker })
25
+ }
26
+
27
+ /// Create client with custom timeout (in seconds)
28
+ ///
29
+ /// Use for long-running operations like image builds.
30
+ /// Default timeout is 120 seconds; build timeout should be 600+ seconds.
31
+ pub fn with_timeout(timeout_secs: u64) -> Result<Self, DockerError> {
32
+ let docker = Docker::connect_with_local_defaults()
33
+ .map_err(|e| DockerError::Connection(e.to_string()))?
34
+ .with_timeout(std::time::Duration::from_secs(timeout_secs));
35
+
36
+ Ok(Self { inner: docker })
37
+ }
38
+
39
+ /// Verify connection to Docker daemon
40
+ ///
41
+ /// Returns Ok(()) if connected, descriptive error otherwise.
42
+ pub async fn verify_connection(&self) -> Result<(), DockerError> {
43
+ self.inner.ping().await.map_err(DockerError::from)?;
44
+ Ok(())
45
+ }
46
+
47
+ /// Get Docker version info (useful for debugging)
48
+ pub async fn version(&self) -> Result<String, DockerError> {
49
+ let version = self.inner.version().await.map_err(DockerError::from)?;
50
+
51
+ let version_str = format!(
52
+ "Docker {} (API {})",
53
+ version.version.unwrap_or_else(|| "unknown".to_string()),
54
+ version.api_version.unwrap_or_else(|| "unknown".to_string())
55
+ );
56
+
57
+ Ok(version_str)
58
+ }
59
+
60
+ /// Access inner Bollard client for advanced operations
61
+ pub fn inner(&self) -> &Docker {
62
+ &self.inner
63
+ }
64
+ }
65
+
66
+ #[cfg(test)]
67
+ mod tests {
68
+ use super::*;
69
+
70
+ #[test]
71
+ fn docker_client_creation_does_not_panic() {
72
+ // This test just verifies the code compiles and doesn't panic
73
+ // Actual connection test requires Docker to be running
74
+ let result = DockerClient::new();
75
+ // We don't assert success because Docker may not be running in CI
76
+ drop(result);
77
+ }
78
+
79
+ #[test]
80
+ fn docker_client_with_timeout_does_not_panic() {
81
+ let result = DockerClient::with_timeout(600);
82
+ drop(result);
83
+ }
84
+ }