mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-07 17:09:13 -10:00
fix(ci): isolate host-tool metrics and avoid smoke pipe races
The no-host-tools catalog fixture used an empty PATH, which still permits Windows executable discovery outside PATH. Its two fresh shell samples could differ by Python-backed code_execution despite both recording bash. Reuse the existing recorded-host capability pin in all test builds and hold an empty set while measuring this declared profile. Production discovery, catalog assertions, tool identities and budgets remain unchanged. Mobile smoke piped echo into grep -q under pipefail. A successful early grep exit could give echo SIGPIPE, falsely fail the positive assertion or falsely pass the negative assertion. Feed all three checks with here-strings. Validation on canonical main248634f plus exactly these three files: - Existing focused Rust tests: 2 passed, 0 failed, 15349 filtered out. The inherited-shell check retains its two fresh metric subprocesses. - Existing tool-catalog measurement: entire JSON equals the current budget, including all mode/surface counts, bytes, names and identities. - Default-feature production CLI build: exit 0; the resulting executable passed all 9 existing mobile HTTP/auth/binding assertions, 0 failed. Both smoke listeners were absent afterward. No provider turn was sent. - npm test: 540 Node + 772 Vitest = 1312 passed, 0 failed, 7 skipped. - npm run check:web, scoped rustfmt, Bash syntax, ShellCheck and diff check all exited 0. Compiler inputs remained frozen through verification. The first isolated smoke launch stopped before HTTP assertions because its macOS daemon socket path exceeded 103 bytes. A short CODEWHALE_HOME inside the same fresh temporary root fixed the launcher; only the failed smoke was rerun, using the unchanged qualified binary. The original failure is retained. Existing nonfatal Apple compact-unwind linker warning remains. No new tests or code comments; existing misleading prose is corrected. Fresh hosted Windows execution remains required. No publication or deploy. Refs CW-6439 Signed-off-by: Hunter Bown <[email protected]>
This commit is contained in:
1 parent
248634f868
commit
51c00f231a
3 files changed
+18
-17
No files matched your search
@@ -451,17 +451,18 @@ async fn measure_production_mode_tool_catalogs() -> serde_json::Value {
|
||||
let _userprofile = EnvVarGuard::set("USERPROFILE", &home);
|
||||
let _codewhale_home = EnvVarGuard::set("CODEWHALE_HOME", home.join(".codewhale"));
|
||||
// Interpreter-backed advanced tools are intentionally excluded from this
|
||||
// cross-platform built-in profile. The production planner still owns that
|
||||
// decision; a deliberately nonexistent PATH root makes its real dependency
|
||||
// probes return absent without inheriting the developer or CI host.
|
||||
// cross-platform built-in profile. Pin host capabilities because Windows
|
||||
// system-directory lookup can find interpreters outside PATH. Keep the
|
||||
// nonexistent PATH root for the canonical shell spelling below.
|
||||
let _host_tools = crate::dependencies::pin_recorded_host_tools(Vec::new());
|
||||
let _path = EnvVarGuard::set("PATH", tmp.path().join("no-host-interpreters"));
|
||||
// Shell syntax is part of the tool schema. A bare name plus the empty
|
||||
// PATH resolves to the same `bash` spelling on Unix and Windows without
|
||||
// executing or requiring that interpreter. Do not inherit the host shell.
|
||||
let _shell = EnvVarGuard::set("SHELL", "bash");
|
||||
// The macOS Vision OCR probe is a framework check that ignores PATH, so
|
||||
// the profile neutralizes it explicitly: every host presents no local OCR
|
||||
// capability here, matching the no-host-interpreters PATH pin above.
|
||||
// Keep the explicit OCR-unavailable environment for framework probes that
|
||||
// ignore PATH; the host capability pin above also excludes local OCR from
|
||||
// this profile.
|
||||
let _ocr = EnvVarGuard::set("CODEWHALE_LOCAL_OCR_UNAVAILABLE", "1");
|
||||
|
||||
let api_config = Config {
|
||||
|
||||
@@ -350,10 +350,10 @@ pub fn resolve_pandoc() -> Option<String> {
|
||||
|
||||
/// Whether an optional tool whose backend lives on this host (an interpreter,
|
||||
/// a converter, an OCR engine) is available: `probe` decides, except that a
|
||||
/// conformance replay answers with the recorded host's set so goldens do not
|
||||
/// test fixture answers with the recorded host's set so results do not
|
||||
/// depend on what the machine running them has installed (test builds only).
|
||||
pub(crate) fn host_tool_available(tool: &str, probe: impl FnOnce() -> bool) -> bool {
|
||||
#[cfg(all(test, unix))]
|
||||
#[cfg(test)]
|
||||
if let Some(available) = RECORDED_HOST_TOOLS.with(|cell| {
|
||||
cell.borrow()
|
||||
.as_ref()
|
||||
@@ -361,13 +361,13 @@ pub(crate) fn host_tool_available(tool: &str, probe: impl FnOnce() -> bool) -> b
|
||||
}) {
|
||||
return available;
|
||||
}
|
||||
// Only a conformance replay reads the name.
|
||||
#[cfg(not(all(test, unix)))]
|
||||
// Only test fixtures read the name.
|
||||
#[cfg(not(test))]
|
||||
let _ = tool;
|
||||
probe()
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
#[cfg(test)]
|
||||
thread_local! {
|
||||
static RECORDED_HOST_TOOLS: std::cell::RefCell<Option<Vec<String>>> =
|
||||
const { std::cell::RefCell::new(None) };
|
||||
@@ -375,16 +375,16 @@ thread_local! {
|
||||
|
||||
/// Pin [`host_tool_available`] on this thread to a recorded host's tools until
|
||||
/// the guard drops.
|
||||
#[cfg(all(test, unix))]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn pin_recorded_host_tools(tools: Vec<String>) -> RecordedHostToolsGuard {
|
||||
RECORDED_HOST_TOOLS.with(|cell| *cell.borrow_mut() = Some(tools));
|
||||
RecordedHostToolsGuard
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
#[cfg(test)]
|
||||
pub(crate) struct RecordedHostToolsGuard;
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
#[cfg(test)]
|
||||
impl Drop for RecordedHostToolsGuard {
|
||||
fn drop(&mut self) {
|
||||
RECORDED_HOST_TOOLS.with(|cell| *cell.borrow_mut() = None);
|
||||
|
||||
@@ -103,7 +103,7 @@ assert_body_contains() {
|
||||
local body
|
||||
body=$(curl "${curl_args[@]}" "$url" 2>/dev/null || true)
|
||||
|
||||
if echo "$body" | grep -q "$substring"; then
|
||||
if grep -q -- "$substring" <<< "$body"; then
|
||||
pass "$method $path body contains '$substring'"
|
||||
else
|
||||
fail "$method $path body missing '$substring'"
|
||||
@@ -121,7 +121,7 @@ assert_body_not_contains() {
|
||||
local body
|
||||
body=$(curl "${curl_args[@]}" "$url" 2>/dev/null || true)
|
||||
|
||||
if echo "$body" | grep -q "$substring"; then
|
||||
if grep -q -- "$substring" <<< "$body"; then
|
||||
fail "$method $path body unexpectedly contains '$substring'"
|
||||
else
|
||||
pass "$method $path body does not contain '$substring'"
|
||||
@@ -209,7 +209,7 @@ else
|
||||
fail "mobile unexpectedly accepted a 0.0.0.0 binding"
|
||||
fi
|
||||
|
||||
if echo "$BIND_OUTPUT" | grep -qi "loopback-only"; then
|
||||
if grep -qi -- "loopback-only" <<< "$BIND_OUTPUT"; then
|
||||
pass "rejection explains the loopback-only boundary"
|
||||
else
|
||||
fail "rejection missing loopback-only guidance"
|
||||
|
||||
Reference in new issue
Block a user