From 18f98cce545e2ae06e3de495f6ec1dd7e7620abd Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 9 Nov 2026 08:23:58 +0000 Subject: [PATCH 1/2] fix(crowdgpt): keep the runner files inside the crate so node-only builds compile The c0mpute.com deploy builds from a context with only node/ copied, so the include_str! of plugins/crowdgpt/runner broke every dev2 deploy since #181. The Dockerfile and watchdog now live in node/crates/c0mpute-core/crowdgpt-runner; the image workflow builds from there. No behavior change. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/crowdgpt-runner.yml | 18 +++++++++--------- Dockerfile.web | 3 --- .../c0mpute-core/crowdgpt-runner}/Dockerfile | 0 .../crowdgpt-runner}/c0mpute-crowdgpt-run | 0 node/crates/c0mpute-core/src/crowdgpt.rs | 9 +++++---- plugins/crowdgpt/module.toml | 2 +- 6 files changed, 15 insertions(+), 17 deletions(-) rename {plugins/crowdgpt/runner => node/crates/c0mpute-core/crowdgpt-runner}/Dockerfile (100%) rename {plugins/crowdgpt/runner => node/crates/c0mpute-core/crowdgpt-runner}/c0mpute-crowdgpt-run (100%) diff --git a/.github/workflows/crowdgpt-runner.yml b/.github/workflows/crowdgpt-runner.yml index bce556f8..f36c41a3 100644 --- a/.github/workflows/crowdgpt-runner.yml +++ b/.github/workflows/crowdgpt-runner.yml @@ -1,4 +1,4 @@ -# Build and publish the CrowdGPT runner image (plugins/crowdgpt/runner). +# Build and publish the CrowdGPT runner image (node/crates/c0mpute-core/crowdgpt-runner). # # The image holds CrowdGPT's client (GPL-3.0, unmodified) at the commit pinned # by ARG CROWDGPT_COMMIT in the Dockerfile, which must equal CROWDGPT_COMMIT in @@ -18,11 +18,11 @@ on: push: branches: [master] paths: - - "plugins/crowdgpt/runner/**" + - "node/crates/c0mpute-core/crowdgpt-runner/**" - ".github/workflows/crowdgpt-runner.yml" pull_request: paths: - - "plugins/crowdgpt/runner/**" + - "node/crates/c0mpute-core/crowdgpt-runner/**" - ".github/workflows/crowdgpt-runner.yml" workflow_dispatch: @@ -38,9 +38,9 @@ jobs: - name: Watchdog parses and the pin is consistent run: | set -eu - sh -n plugins/crowdgpt/runner/c0mpute-crowdgpt-run - shellcheck -s sh plugins/crowdgpt/runner/c0mpute-crowdgpt-run || true - pin=$(sed -n 's/^ARG CROWDGPT_COMMIT=//p' plugins/crowdgpt/runner/Dockerfile) + sh -n node/crates/c0mpute-core/crowdgpt-runner/c0mpute-crowdgpt-run + shellcheck -s sh node/crates/c0mpute-core/crowdgpt-runner/c0mpute-crowdgpt-run || true + pin=$(sed -n 's/^ARG CROWDGPT_COMMIT=//p' node/crates/c0mpute-core/crowdgpt-runner/Dockerfile) grep -q "CROWDGPT_COMMIT: &str = \"$pin\"" node/crates/c0mpute-core/src/crowdgpt.rs grep -q "crowdgpt-$(echo "$pin" | cut -c1-12)" plugins/crowdgpt/module.toml echo "pinned CrowdGPT commit: $pin" @@ -73,7 +73,7 @@ jobs: id: tags run: | set -eu - pin=$(sed -n 's/^ARG CROWDGPT_COMMIT=//p' plugins/crowdgpt/runner/Dockerfile) + pin=$(sed -n 's/^ARG CROWDGPT_COMMIT=//p' node/crates/c0mpute-core/crowdgpt-runner/Dockerfile) short=$(echo "$pin" | cut -c1-12) echo "pin=${pin}" >> "$GITHUB_OUTPUT" echo "tags=${IMAGE}:crowdgpt-${short},${IMAGE}:stable,${IMAGE}:${GITHUB_SHA}" >> "$GITHUB_OUTPUT" @@ -84,8 +84,8 @@ jobs: with: labels: | org.opencontainers.image.source=https://github.com/${{ github.repository }} - context: plugins/crowdgpt/runner - file: plugins/crowdgpt/runner/Dockerfile + context: node/crates/c0mpute-core/crowdgpt-runner + file: node/crates/c0mpute-core/crowdgpt-runner/Dockerfile load: ${{ github.event_name == 'pull_request' }} push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.tags.outputs.tags }} diff --git a/Dockerfile.web b/Dockerfile.web index d76a362d..bfdf9957 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -15,9 +15,6 @@ FROM rust:1.92-bookworm AS rustbuild WORKDIR /src COPY Cargo.toml Cargo.lock ./ COPY node ./node -# The crowdgpt runner's Dockerfile and watchdog are compiled into the binary -# (`c0mpute crowdgpt setup --build`). -COPY plugins/crowdgpt/runner ./plugins/crowdgpt/runner RUN cargo build --release --bin c0mpute # ── stage 2: build the Next.js site (Bun) ──────────────────────────────── diff --git a/plugins/crowdgpt/runner/Dockerfile b/node/crates/c0mpute-core/crowdgpt-runner/Dockerfile similarity index 100% rename from plugins/crowdgpt/runner/Dockerfile rename to node/crates/c0mpute-core/crowdgpt-runner/Dockerfile diff --git a/plugins/crowdgpt/runner/c0mpute-crowdgpt-run b/node/crates/c0mpute-core/crowdgpt-runner/c0mpute-crowdgpt-run similarity index 100% rename from plugins/crowdgpt/runner/c0mpute-crowdgpt-run rename to node/crates/c0mpute-core/crowdgpt-runner/c0mpute-crowdgpt-run diff --git a/node/crates/c0mpute-core/src/crowdgpt.rs b/node/crates/c0mpute-core/src/crowdgpt.rs index 8027aa62..6d5725e4 100644 --- a/node/crates/c0mpute-core/src/crowdgpt.rs +++ b/node/crates/c0mpute-core/src/crowdgpt.rs @@ -27,7 +27,7 @@ //! The client is GPL-3.0. c0mpute never vendors, links or imports it: it runs //! as a separate process in its own container, built from CrowdGPT's public //! repository at the reviewed commit [`CROWDGPT_COMMIT`] by our MIT Dockerfile -//! (plugins/crowdgpt/runner). c0mpute talks to it only through `docker run` / +//! (node/crates/c0mpute-core/crowdgpt-runner). c0mpute talks to it only through `docker run` / //! `docker stop` and its log lines. //! //! # Containment @@ -96,10 +96,11 @@ pub fn pull_ref() -> String { format!("{RUNNER_REPOSITORY}:crowdgpt-{}", &CROWDGPT_COMMIT[..12]) } -/// Our Dockerfile and watchdog, embedded so `c0mpute crowdgpt setup --build` +/// Our Dockerfile and watchdog (node/crates/c0mpute-core/crowdgpt-runner, +/// inside the crate so builds that copy only `node/` still compile), embedded so `c0mpute crowdgpt setup --build` /// needs only docker (CrowdGPT's source is fetched inside the build). -pub const DOCKERFILE: &str = include_str!("../../../../plugins/crowdgpt/runner/Dockerfile"); -pub const WATCHDOG: &str = include_str!("../../../../plugins/crowdgpt/runner/c0mpute-crowdgpt-run"); +pub const DOCKERFILE: &str = include_str!("../crowdgpt-runner/Dockerfile"); +pub const WATCHDOG: &str = include_str!("../crowdgpt-runner/c0mpute-crowdgpt-run"); /// `[crowdgpt]` in config.toml. Off by default. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] diff --git a/plugins/crowdgpt/module.toml b/plugins/crowdgpt/module.toml index 51ae3a83..9fd3b7f6 100644 --- a/plugins/crowdgpt/module.toml +++ b/plugins/crowdgpt/module.toml @@ -19,7 +19,7 @@ # # GPL isolation. The client is GPL-3.0 and is never vendored, linked or # imported: it runs unmodified as a separate process in a container built by -# runner/Dockerfile (MIT) from CrowdGPT's repository at one reviewed commit +# node/crates/c0mpute-core/crowdgpt-runner/Dockerfile (MIT) from CrowdGPT's repository at one reviewed commit # (CROWDGPT_COMMIT in crowdgpt.rs, also the image's # org.c0mpute.crowdgpt.commit label, checked before every run). CI publishes # it as ghcr.io/profullstack/c0mpute-crowdgpt-runner:crowdgpt- From d1407e1ad28ddba0c820481fe270b0b1505b8e4f Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 9 Nov 2026 08:27:57 +0000 Subject: [PATCH 2/2] fix(crowdgpt): build on Windows (uid/gid and 0600 only on Unix) v0.2.89's windows-x86_64 release build failed on std::os::unix in crowdgpt.rs. Co-Authored-By: Claude Opus 5.5 --- node/crates/c0mpute-cli/src/crowdgpt.rs | 13 +++++++++---- node/crates/c0mpute-core/src/crowdgpt.rs | 9 ++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/node/crates/c0mpute-cli/src/crowdgpt.rs b/node/crates/c0mpute-cli/src/crowdgpt.rs index 46893212..c547b8eb 100644 --- a/node/crates/c0mpute-cli/src/crowdgpt.rs +++ b/node/crates/c0mpute-cli/src/crowdgpt.rs @@ -171,8 +171,7 @@ pub async fn run(cmd: CrowdgptCmd, config_path: &Path) -> Result<()> { } fn owner_args(dir: &Path) -> Vec { - use std::os::unix::fs::MetadataExt; - std::fs::metadata(dir).map(|m| vec!["--user".to_string(), format!("{}:{}", m.uid(), m.gid())]).unwrap_or_default() + cg::owner_of(dir).map(|(u, g)| vec!["--user".to_string(), format!("{u}:{g}")]).unwrap_or_default() } fn credited_as() -> String { @@ -190,11 +189,17 @@ fn credited_as() -> String { fn write_token(token: &str) -> Result<()> { use std::io::Write; - use std::os::unix::fs::OpenOptionsExt; anyhow::ensure!(!token.is_empty() && token.len() < 4096 && !token.chars().any(char::is_whitespace), "that does not look like a token"); let p = cg::token_path(); std::fs::create_dir_all(p.parent().unwrap())?; - let mut f = std::fs::OpenOptions::new().write(true).create(true).truncate(true).mode(0o600).open(&p)?; + let mut o = std::fs::OpenOptions::new(); + o.write(true).create(true).truncate(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + o.mode(0o600); + } + let mut f = o.open(&p)?; f.write_all(token.as_bytes())?; Ok(()) } diff --git a/node/crates/c0mpute-core/src/crowdgpt.rs b/node/crates/c0mpute-core/src/crowdgpt.rs index 6d5725e4..cdf23d1e 100644 --- a/node/crates/c0mpute-core/src/crowdgpt.rs +++ b/node/crates/c0mpute-core/src/crowdgpt.rs @@ -455,11 +455,18 @@ pub fn decide(running: bool, busy: bool, blocked: bool, idle_for: Duration, idle } } -fn owner_of(dir: &Path) -> Option<(u32, u32)> { +/// The uid:gid owning `dir`, which the container runs as (`None` off Unix). +#[cfg(unix)] +pub fn owner_of(dir: &Path) -> Option<(u32, u32)> { use std::os::unix::fs::MetadataExt; std::fs::metadata(dir).ok().map(|m| (m.uid(), m.gid())) } +#[cfg(not(unix))] +pub fn owner_of(_dir: &Path) -> Option<(u32, u32)> { + None +} + /// Running crowdgpt containers (by [`LABEL`]). pub async fn running_containers() -> Vec { let out = crate::container::command().args(["ps", "-q", "--filter", &format!("label={LABEL}")]).stderr(std::process::Stdio::null()).output().await;