Skip to content

Commit d0664bf

Browse files
authored
Merge pull request #268 from spacedriveapp/feat/browser-chrome-fetcher
feat: auto-download Chrome via fetcher, unify Docker image, fix singleton lock
2 parents fdab751 + 3c6e12a commit d0664bf

File tree

9 files changed

+310
-205
lines changed

9 files changed

+310
-205
lines changed

.github/workflows/release.yml

Lines changed: 8 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -115,27 +115,15 @@ jobs:
115115
id: platform
116116
run: echo "pair=$(echo ${{ matrix.platform }} | tr '/' '-')" >> "$GITHUB_OUTPUT"
117117

118-
- name: Build and push slim
118+
- name: Build and push
119119
uses: docker/build-push-action@v6
120120
with:
121121
context: .
122-
target: slim
123122
platforms: ${{ matrix.platform }}
124123
push: true
125-
tags: ${{ env.IMAGE }}:slim-${{ steps.platform.outputs.pair }}
126-
cache-from: type=gha,scope=slim-${{ steps.platform.outputs.pair }}
127-
cache-to: type=gha,mode=max,scope=slim-${{ steps.platform.outputs.pair }}
128-
129-
- name: Build and push full
130-
uses: docker/build-push-action@v6
131-
with:
132-
context: .
133-
target: full
134-
platforms: ${{ matrix.platform }}
135-
push: true
136-
tags: ${{ env.IMAGE }}:full-${{ steps.platform.outputs.pair }}
137-
cache-from: type=gha,scope=full-${{ steps.platform.outputs.pair }}
138-
cache-to: type=gha,mode=max,scope=full-${{ steps.platform.outputs.pair }}
124+
tags: ${{ env.IMAGE }}:build-${{ steps.platform.outputs.pair }}
125+
cache-from: type=gha,scope=build-${{ steps.platform.outputs.pair }}
126+
cache-to: type=gha,mode=max,scope=build-${{ steps.platform.outputs.pair }}
139127

140128
merge-docker:
141129
needs: build-docker
@@ -155,22 +143,13 @@ jobs:
155143
username: ${{ github.actor }}
156144
password: ${{ secrets.GITHUB_TOKEN }}
157145

158-
- name: Create slim multi-arch manifest
159-
run: |
160-
docker buildx imagetools create \
161-
--tag ${{ env.IMAGE }}:${{ needs.build-docker.outputs.version }}-slim \
162-
--tag ${{ env.IMAGE }}:slim \
163-
${{ env.IMAGE }}:slim-linux-amd64 \
164-
${{ env.IMAGE }}:slim-linux-arm64
165-
166-
- name: Create full multi-arch manifest
146+
- name: Create multi-arch manifest
167147
run: |
168148
docker buildx imagetools create \
169-
--tag ${{ env.IMAGE }}:${{ needs.build-docker.outputs.version }}-full \
170-
--tag ${{ env.IMAGE }}:full \
149+
--tag ${{ env.IMAGE }}:${{ needs.build-docker.outputs.version }} \
171150
--tag ${{ env.IMAGE }}:latest \
172-
${{ env.IMAGE }}:full-linux-amd64 \
173-
${{ env.IMAGE }}:full-linux-arm64
151+
${{ env.IMAGE }}:build-linux-amd64 \
152+
${{ env.IMAGE }}:build-linux-arm64
174153
175154
- name: Log in to Fly registry
176155
if: github.repository_owner == 'spacedriveapp'

Cargo.lock

Lines changed: 49 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ arrow-array = "57.3.0"
132132
arrow-schema = "57.3.0"
133133

134134
# Browser automation
135-
chromiumoxide = { version = "0.8", features = ["tokio-runtime"], default-features = false }
135+
chromiumoxide = { version = "0.8", features = ["tokio-runtime", "_fetcher-rustls-tokio"], default-features = false }
136136
chromiumoxide_cdp = "0.8"
137137

138138
# Templating for prompts

Dockerfile

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,38 +43,21 @@ RUN SPACEBOT_SKIP_FRONTEND_BUILD=1 cargo build --release \
4343
&& mv /build/target/release/spacebot /usr/local/bin/spacebot \
4444
&& cargo clean -p spacebot --release --target-dir /build/target
4545

46-
# ---- Slim stage ----
47-
# Minimal runtime with just the binary. No browser.
48-
FROM debian:bookworm-slim AS slim
46+
# ---- Runtime stage ----
47+
# Minimal runtime with Chrome runtime libraries for fetcher-downloaded Chromium.
48+
# Chrome itself is downloaded on first browser tool use and cached on the volume.
49+
FROM debian:bookworm-slim
4950

5051
RUN apt-get update && apt-get install -y --no-install-recommends \
5152
ca-certificates \
5253
libsqlite3-0 \
5354
curl \
5455
gh \
5556
bubblewrap \
56-
&& rm -rf /var/lib/apt/lists/*
57-
58-
COPY --from=builder /usr/local/bin/spacebot /usr/local/bin/spacebot
59-
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
60-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
61-
62-
ENV SPACEBOT_DIR=/data
63-
ENV SPACEBOT_DEPLOYMENT=docker
64-
EXPOSE 19898 18789
65-
66-
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
67-
CMD curl -f http://localhost:19898/api/health || exit 1
68-
69-
ENTRYPOINT ["docker-entrypoint.sh"]
70-
CMD ["spacebot", "start", "--foreground"]
71-
72-
# ---- Full stage ----
73-
# Slim + Chromium for browser workers.
74-
FROM slim AS full
75-
76-
RUN apt-get update && apt-get install -y --no-install-recommends \
77-
chromium \
57+
openssh-server \
58+
# Chrome runtime dependencies — required whether Chrome is system-installed
59+
# or downloaded by the built-in fetcher. The fetcher provides the browser
60+
# binary; these are the shared libraries it links against.
7861
fonts-liberation \
7962
libnss3 \
8063
libatk-bridge2.0-0 \
@@ -91,5 +74,16 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
9174
libxtst6 \
9275
&& rm -rf /var/lib/apt/lists/*
9376

94-
ENV CHROME_PATH=/usr/bin/chromium
95-
ENV CHROME_FLAGS="--no-sandbox --disable-dev-shm-usage --disable-gpu"
77+
COPY --from=builder /usr/local/bin/spacebot /usr/local/bin/spacebot
78+
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
79+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
80+
81+
ENV SPACEBOT_DIR=/data
82+
ENV SPACEBOT_DEPLOYMENT=docker
83+
EXPOSE 19898 18789
84+
85+
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
86+
CMD curl -f http://localhost:19898/api/health || exit 1
87+
88+
ENTRYPOINT ["docker-entrypoint.sh"]
89+
CMD ["spacebot", "start", "--foreground"]

fly.staging.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ primary_region = "iad"
33

44
[build]
55
dockerfile = "Dockerfile"
6-
target = "full"
76

87
[env]
98
SPACEBOT_DIR = "/data"

fly.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ primary_region = "iad"
33

44
[build]
55
dockerfile = "Dockerfile"
6-
target = "full"
76

87
[env]
98
SPACEBOT_DIR = "/data"

src/config.rs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,9 @@ pub struct BrowserConfig {
870870
pub executable_path: Option<String>,
871871
/// Directory for storing screenshots and other browser artifacts.
872872
pub screenshot_dir: Option<PathBuf>,
873+
/// Directory for caching a fetcher-downloaded Chromium binary.
874+
/// Populated from `{instance_dir}/chrome_cache` during config resolution.
875+
pub chrome_cache_dir: PathBuf,
873876
}
874877

875878
impl Default for BrowserConfig {
@@ -880,6 +883,7 @@ impl Default for BrowserConfig {
880883
evaluate_enabled: false,
881884
executable_path: None,
882885
screenshot_dir: None,
886+
chrome_cache_dir: PathBuf::from("chrome_cache"),
883887
}
884888
}
885889
}
@@ -4121,10 +4125,13 @@ impl Config {
41214125
let mut api = ApiConfig::default();
41224126
api.bind = hosted_api_bind(api.bind);
41234127

4128+
let mut defaults = DefaultsConfig::default();
4129+
defaults.browser.chrome_cache_dir = instance_dir.join("chrome_cache");
4130+
41244131
Ok(Self {
41254132
instance_dir: instance_dir.to_path_buf(),
41264133
llm,
4127-
defaults: DefaultsConfig::default(),
4134+
defaults,
41284135
agents,
41294136
links: Vec::new(),
41304137
groups: Vec::new(),
@@ -4729,23 +4736,31 @@ impl Config {
47294736
.unwrap_or(base_defaults.warmup.startup_delay_secs),
47304737
})
47314738
.unwrap_or(base_defaults.warmup),
4732-
browser: toml
4733-
.defaults
4734-
.browser
4735-
.map(|b| {
4736-
let base = &base_defaults.browser;
4737-
BrowserConfig {
4738-
enabled: b.enabled.unwrap_or(base.enabled),
4739-
headless: b.headless.unwrap_or(base.headless),
4740-
evaluate_enabled: b.evaluate_enabled.unwrap_or(base.evaluate_enabled),
4741-
executable_path: b.executable_path.or_else(|| base.executable_path.clone()),
4742-
screenshot_dir: b
4743-
.screenshot_dir
4744-
.map(PathBuf::from)
4745-
.or_else(|| base.screenshot_dir.clone()),
4746-
}
4747-
})
4748-
.unwrap_or_else(|| base_defaults.browser.clone()),
4739+
browser: {
4740+
let chrome_cache_dir = instance_dir.join("chrome_cache");
4741+
toml.defaults
4742+
.browser
4743+
.map(|b| {
4744+
let base = &base_defaults.browser;
4745+
BrowserConfig {
4746+
enabled: b.enabled.unwrap_or(base.enabled),
4747+
headless: b.headless.unwrap_or(base.headless),
4748+
evaluate_enabled: b.evaluate_enabled.unwrap_or(base.evaluate_enabled),
4749+
executable_path: b
4750+
.executable_path
4751+
.or_else(|| base.executable_path.clone()),
4752+
screenshot_dir: b
4753+
.screenshot_dir
4754+
.map(PathBuf::from)
4755+
.or_else(|| base.screenshot_dir.clone()),
4756+
chrome_cache_dir: chrome_cache_dir.clone(),
4757+
}
4758+
})
4759+
.unwrap_or_else(|| BrowserConfig {
4760+
chrome_cache_dir,
4761+
..base_defaults.browser.clone()
4762+
})
4763+
},
47494764
mcp: default_mcp,
47504765
brave_search_key: toml
47514766
.defaults
@@ -4935,6 +4950,7 @@ impl Config {
49354950
.screenshot_dir
49364951
.map(PathBuf::from)
49374952
.or_else(|| defaults.browser.screenshot_dir.clone()),
4953+
chrome_cache_dir: defaults.browser.chrome_cache_dir.clone(),
49384954
}),
49394955
mcp: match a.mcp {
49404956
Some(mcp_servers) => Some(

0 commit comments

Comments
 (0)