From 19febf1f79ff5fe1fe9d9a5d116a3621ef570d6a Mon Sep 17 00:00:00 2001 From: Mark <399551+mwiebe@users.noreply.github.com> Date: Mon, 11 May 2026 15:20:25 -0700 Subject: [PATCH] fix(sessions): allow `cargo publish` by renaming nested helper manifest Cargo refuses to package any subdirectory containing a `Cargo.toml`, which caused `cargo publish -p openjd-sessions` to exclude `src/helper/` and fail during verification. Rename the helper manifest to `Cargo.toml.bundled` so Cargo includes it in the tarball, and update `build.rs` to copy the helper tree into OUT_DIR and restore the manifest name before building. Signed-off-by: Mark <399551+mwiebe@users.noreply.github.com> --- .github/workflows/ci.yml | 7 +++ .gitignore | 1 + crates/openjd-sessions/Cargo.toml | 8 +++ crates/openjd-sessions/build.rs | 54 ++++++++++++++----- .../helper/{Cargo.toml => Cargo.bundled.toml} | 0 5 files changed, 56 insertions(+), 14 deletions(-) rename crates/openjd-sessions/src/helper/{Cargo.toml => Cargo.bundled.toml} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4e349fb..bf993bd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,8 @@ jobs: rustup override set nightly - name: Run cargo fmt run: cargo fmt --all -- --check + - name: Restore helper manifest + run: cp crates/openjd-sessions/src/helper/Cargo.bundled.toml crates/openjd-sessions/src/helper/Cargo.toml - name: Run helper cargo fmt run: cargo fmt --manifest-path crates/openjd-sessions/src/helper/Cargo.toml --all -- --check @@ -44,6 +46,8 @@ jobs: - uses: actions/checkout@v6 - name: Install cargo-deny run: cargo install cargo-deny --locked + - name: Restore helper manifest + run: cp crates/openjd-sessions/src/helper/Cargo.bundled.toml crates/openjd-sessions/src/helper/Cargo.toml # Licenses, bans, and sources apply to every crate we compile, # including dev-dependencies, so scan the full graph. - name: Run cargo deny check licenses, bans, sources (workspace) @@ -138,6 +142,9 @@ jobs: # openjd-for-js is a wasm32 crate that has its own dedicated workflow # (openjd-for-js.yml); compiling it natively here wastes ~45-60s # recompiling wasm-bindgen, js-sys, web-sys, and serde-wasm-bindgen. + - name: Restore helper manifest + shell: bash + run: cp crates/openjd-sessions/src/helper/Cargo.bundled.toml crates/openjd-sessions/src/helper/Cargo.toml - name: Build workspace (lib, bin, tests) run: cargo test --release --workspace --exclude openjd-for-js --all-targets --no-run - name: Run cargo clippy diff --git a/.gitignore b/.gitignore index 1278962e..2558bc5b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ package*.json !crates/openjd-for-js/package.json !crates/openjd-for-js/package-lock.json crates/openjd-sessions/src/helper/target/ +crates/openjd-sessions/src/helper/Cargo.toml # Build artifacts crates/openjd-for-js/node_modules/ diff --git a/crates/openjd-sessions/Cargo.toml b/crates/openjd-sessions/Cargo.toml index af4bd287..ce3f5a3b 100644 --- a/crates/openjd-sessions/Cargo.toml +++ b/crates/openjd-sessions/Cargo.toml @@ -9,6 +9,14 @@ repository.workspace = true homepage.workspace = true readme = "README.md" description = "Open Job Description sessions — local job execution runtime" +include = [ + "/src/**", + "/tests/**", + "/build.rs", + "/README.md", + "/LICENSE-*", + "/NOTICE", +] keywords = ["openjd", "openjobdescription", "session", "subprocess", "runtime"] categories = ["command-line-utilities", "os"] build = "build.rs" diff --git a/crates/openjd-sessions/build.rs b/crates/openjd-sessions/build.rs index 4c6b6519..b96cf607 100644 --- a/crates/openjd-sessions/build.rs +++ b/crates/openjd-sessions/build.rs @@ -2,7 +2,24 @@ // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use std::path::PathBuf; +use std::path::{Path, PathBuf}; + +fn copy_dir_recursive(src: &Path, dst: &Path) { + std::fs::create_dir_all(dst).unwrap(); + for entry in std::fs::read_dir(src).unwrap() { + let entry = entry.unwrap(); + let ty = entry.file_type().unwrap(); + let dest_path = dst.join(entry.file_name()); + if ty.is_dir() { + if entry.file_name() == "target" { + continue; + } + copy_dir_recursive(&entry.path(), &dest_path); + } else { + std::fs::copy(entry.path(), &dest_path).unwrap(); + } + } +} fn main() { let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); @@ -12,10 +29,11 @@ fn main() { let helper_dir = manifest_dir.join("src/helper"); let helper_out = out_dir.join("openjd_helper"); - // Rerun only when helper sources change. Watching the whole `src/helper/` - // directory would include `src/helper/target/` which this script writes - // into, causing spurious re-runs on every cargo invocation (and - // recompilation of openjd-sessions + openjd-cli every time). + // Rerun only when helper sources change. + println!( + "cargo:rerun-if-changed={}", + helper_dir.join("Cargo.bundled.toml").display() + ); println!( "cargo:rerun-if-changed={}", helper_dir.join("Cargo.toml").display() @@ -33,12 +51,28 @@ fn main() { let is_windows = target.contains("windows") || cfg!(windows); if is_unix || is_windows { + // Copy the helper source tree into OUT_DIR so we never write into the + // source tree. The manifest is stored as `Cargo.bundled.toml` so that + // cargo doesn't treat src/helper/ as a separate crate during packaging + // (its check matches the literal filename `Cargo.toml`), while keeping + // the `.toml` extension so editor TOML tooling still works. + // If `Cargo.toml` exists (local dev before rename), use it as-is. + let build_src = out_dir.join("helper_src"); + copy_dir_recursive(&helper_dir, &build_src); + + let manifest_in_build = build_src.join("Cargo.toml"); + if !manifest_in_build.exists() { + let bundled = build_src.join("Cargo.bundled.toml"); + std::fs::rename(&bundled, &manifest_in_build) + .expect("Failed to rename Cargo.bundled.toml to Cargo.toml"); + } + let status = std::process::Command::new("cargo") .args([ "build", "--release", "--manifest-path", - &helper_dir.join("Cargo.toml").to_string_lossy(), + &manifest_in_build.to_string_lossy(), "--target-dir", &out_dir.join("helper_build").to_string_lossy(), "--target", @@ -60,20 +94,12 @@ fn main() { .join(binary_name); std::fs::copy(&built, &helper_out).expect("Failed to copy helper binary"); - // Expose the built helper binary path to integration tests via - // env!("OPENJD_HELPER_BINARY_PATH"). This keeps the helper binary - // inside OUT_DIR where it belongs, instead of copying it back into - // the source tree (which would dirty the cargo fingerprint on every - // run). println!( "cargo:rustc-env=OPENJD_HELPER_BINARY_PATH={}", built.display() ); } else { - // Unsupported platform: write empty placeholder so include_bytes! doesn't fail std::fs::write(&helper_out, b"").expect("Failed to write placeholder"); - // Still set the env var so env!() compiles; tests that need the helper - // will skip on unsupported platforms. println!( "cargo:rustc-env=OPENJD_HELPER_BINARY_PATH={}", helper_out.display() diff --git a/crates/openjd-sessions/src/helper/Cargo.toml b/crates/openjd-sessions/src/helper/Cargo.bundled.toml similarity index 100% rename from crates/openjd-sessions/src/helper/Cargo.toml rename to crates/openjd-sessions/src/helper/Cargo.bundled.toml