diff --git a/.github/workflows/crowdgpt-runner.yml b/.github/workflows/crowdgpt-runner.yml index bce556f..f36c41a 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 d76a362..bfdf995 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/node/crates/c0mpute-cli/src/crowdgpt.rs b/node/crates/c0mpute-cli/src/crowdgpt.rs index 4689321..c547b8e 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/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 8027aa6..cdf23d1 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)] @@ -454,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; diff --git a/plugins/crowdgpt/module.toml b/plugins/crowdgpt/module.toml index 51ae3a8..9fd3b7f 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-