Skip to content

Commit 61479ac

Browse files
Merge branch 'codex/full-rust-ci' into codex/hosted-review-phase-1
Signed-off-by: Timothy Wayne Gregg <Timothy.Gregg@complete.tech> # Conflicts: # src-rust/crates/core/src/claudemd.rs # src-rust/crates/tui/src/image_paste.rs
2 parents 5cadfeb + 66bf26b commit 61479ac

11 files changed

Lines changed: 184 additions & 24 deletions

File tree

.github/workflows/rust-ci.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Rust CI
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'src-rust/**'
7+
- '.github/workflows/rust-ci.yml'
8+
workflow_dispatch:
9+
10+
concurrency:
11+
group: rust-ci-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
permissions:
15+
contents: read
16+
17+
env:
18+
CARGO_TERM_COLOR: always
19+
20+
jobs:
21+
rust:
22+
name: Format, lint, and test
23+
runs-on: ubuntu-latest
24+
timeout-minutes: 45
25+
steps:
26+
- uses: actions/checkout@v5
27+
with:
28+
persist-credentials: false
29+
30+
- name: Install Rust toolchain
31+
uses: dtolnay/rust-toolchain@stable
32+
with:
33+
components: rustfmt, clippy
34+
35+
- name: Install system dependencies
36+
run: |
37+
sudo apt-get update
38+
sudo apt-get install -y libasound2-dev pkg-config
39+
40+
- name: Cache cargo registry and build output
41+
uses: actions/cache@v5
42+
with:
43+
path: |
44+
~/.cargo/registry
45+
~/.cargo/git
46+
src-rust/target
47+
key: rust-ci-cargo-${{ runner.os }}-${{ hashFiles('src-rust/Cargo.lock') }}
48+
restore-keys: rust-ci-cargo-${{ runner.os }}-
49+
50+
- name: Check formatting
51+
working-directory: src-rust
52+
run: cargo fmt --all -- --check
53+
54+
- name: Check workspace
55+
working-directory: src-rust
56+
run: cargo check --workspace --locked
57+
58+
- name: Run Clippy
59+
working-directory: src-rust
60+
run: cargo clippy --workspace --all-targets --locked -- -D warnings
61+
62+
- name: Run tests
63+
working-directory: src-rust
64+
run: cargo test --workspace --locked --quiet

src-rust/crates/commands/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10363,6 +10363,7 @@ pub(crate) mod test_env {
1036310363

1036410364
pub(crate) struct CommandEnvGuard {
1036510365
old_home: Option<String>,
10366+
old_userprofile: Option<String>,
1036610367
old_coven_home: Option<String>,
1036710368
old_user: Option<String>,
1036810369
old_username: Option<String>,
@@ -10376,6 +10377,7 @@ pub(crate) mod test_env {
1037610377
let lock = ENV_LOCK.lock().unwrap_or_else(|err| err.into_inner());
1037710378
let guard = Self {
1037810379
old_home: std::env::var("HOME").ok(),
10380+
old_userprofile: std::env::var("USERPROFILE").ok(),
1037910381
old_coven_home: std::env::var("COVEN_HOME").ok(),
1038010382
old_user: std::env::var("USER").ok(),
1038110383
old_username: std::env::var("USERNAME").ok(),
@@ -10384,6 +10386,7 @@ pub(crate) mod test_env {
1038410386
_lock: lock,
1038510387
};
1038610388
std::env::set_var("HOME", home);
10389+
std::env::set_var("USERPROFILE", home);
1038710390
std::env::set_var("COVEN_HOME", coven_home);
1038810391
match user {
1038910392
Some(value) => std::env::set_var("USER", value),
@@ -10419,6 +10422,10 @@ pub(crate) mod test_env {
1041910422
Some(value) => std::env::set_var("COVEN_HOME", value),
1042010423
None => std::env::remove_var("COVEN_HOME"),
1042110424
}
10425+
match &self.old_userprofile {
10426+
Some(value) => std::env::set_var("USERPROFILE", value),
10427+
None => std::env::remove_var("USERPROFILE"),
10428+
}
1042210429
match &self.old_user {
1042310430
Some(value) => std::env::set_var("USER", value),
1042410431
None => std::env::remove_var("USER"),

src-rust/crates/commands/src/named_commands.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,12 +1012,12 @@ mod tests {
10121012
let home = temp.path().join("home");
10131013
let coven_home = temp.path().join("coven");
10141014
let project = temp.path().join("project");
1015-
let global_agents = home.join(".coven-code").join("agents");
10161015
let project_agents = project.join(".coven-code").join("agents");
1017-
std::fs::create_dir_all(&global_agents).expect("global agents dir");
10181016
std::fs::create_dir_all(&project_agents).expect("project agents dir");
10191017
std::fs::create_dir_all(&coven_home).expect("coven home");
10201018
let _guard = CommandEnvGuard::set(&home, &coven_home, None);
1019+
let global_agents = claurst_core::Settings::config_dir().join("agents");
1020+
std::fs::create_dir_all(&global_agents).expect("global agents dir");
10211021

10221022
let global_agent = global_agents.join("global.md");
10231023
let project_agent = project_agents.join("project.md");

src-rust/crates/core/src/anthropic_cli_import.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,18 @@ pub struct DiscoveredCredential {
4747
// ---------------------------------------------------------------------------
4848

4949
fn claude_code_credentials_path() -> Option<PathBuf> {
50-
Some(dirs::home_dir()?.join(".claude").join(".credentials.json"))
50+
Some(cli_home_dir()?.join(".claude").join(".credentials.json"))
51+
}
52+
53+
fn cli_home_dir() -> Option<PathBuf> {
54+
#[cfg(test)]
55+
if let Ok(home) = std::env::var("COVEN_CODE_TEST_HOME") {
56+
if !home.is_empty() {
57+
return Some(PathBuf::from(home));
58+
}
59+
}
60+
61+
dirs::home_dir()
5162
}
5263

5364
fn ant_credentials_dir() -> Option<PathBuf> {

src-rust/crates/core/src/claudemd.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,10 @@ mod tests {
425425
.unwrap_or_else(|err| err.into_inner());
426426
let original_test_home = std::env::var("COVEN_CODE_TEST_HOME").ok();
427427
let original_home = std::env::var("HOME").ok();
428+
let original_userprofile = std::env::var("USERPROFILE").ok();
428429
std::env::set_var("COVEN_CODE_TEST_HOME", home.path());
429430
std::env::set_var("HOME", home.path());
431+
std::env::set_var("USERPROFILE", home.path());
430432

431433
let files =
432434
load_all_memory_files_with_options(project.path(), &MemoryLoadOptions::hosted_review());
@@ -439,6 +441,10 @@ mod tests {
439441
Some(value) => std::env::set_var("HOME", value),
440442
None => std::env::remove_var("HOME"),
441443
}
444+
match original_userprofile {
445+
Some(value) => std::env::set_var("USERPROFILE", value),
446+
None => std::env::remove_var("USERPROFILE"),
447+
}
442448

443449
assert!(files.iter().all(|file| file.scope != MemoryScope::User));
444450
assert!(files.iter().any(|file| {
@@ -506,8 +512,10 @@ mod tests {
506512
.unwrap_or_else(|err| err.into_inner());
507513
let original_test_home = std::env::var("COVEN_CODE_TEST_HOME").ok();
508514
let original_home = std::env::var("HOME").ok();
515+
let original_userprofile = std::env::var("USERPROFILE").ok();
509516
std::env::set_var("COVEN_CODE_TEST_HOME", home.path());
510517
std::env::set_var("HOME", home.path());
518+
std::env::set_var("USERPROFILE", home.path());
511519

512520
let files = load_all_memory_files_with_options(project.path(), &MemoryLoadOptions::local());
513521

@@ -519,6 +527,10 @@ mod tests {
519527
Some(value) => std::env::set_var("HOME", value),
520528
None => std::env::remove_var("HOME"),
521529
}
530+
match original_userprofile {
531+
Some(value) => std::env::set_var("USERPROFILE", value),
532+
None => std::env::remove_var("USERPROFILE"),
533+
}
522534

523535
assert!(files
524536
.iter()

src-rust/crates/core/src/coven_daemon.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ fn url_quote(input: &str) -> String {
771771
mod tests {
772772
use super::*;
773773
use crate::coven_shared::COVEN_HOME_ENV_LOCK;
774+
#[cfg(unix)]
774775
use std::fs;
775776

776777
/// Guard that temporarily sets `COVEN_HOME` and restores it on drop.
@@ -805,6 +806,7 @@ mod tests {
805806
assert!(DaemonClient::new().is_none());
806807
}
807808

809+
#[cfg(unix)]
808810
#[test]
809811
fn new_returns_some_when_sock_present() {
810812
let _lock = COVEN_HOME_ENV_LOCK
@@ -873,6 +875,7 @@ mod tests {
873875
assert_eq!(s1.active_sessions, 0);
874876
}
875877

878+
#[cfg(unix)]
876879
#[test]
877880
fn familiar_statuses_returns_offline_when_connect_fails() {
878881
let _lock = COVEN_HOME_ENV_LOCK

src-rust/crates/core/src/import_config.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct ImportPaths {
3030

3131
impl ImportPaths {
3232
pub fn detect() -> Self {
33-
let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
33+
let home = import_home_dir();
3434
let claude_dir = home.join(".claude");
3535
let claurst_dir = Settings::config_dir();
3636
Self {
@@ -42,6 +42,17 @@ impl ImportPaths {
4242
}
4343
}
4444

45+
fn import_home_dir() -> PathBuf {
46+
#[cfg(test)]
47+
if let Ok(home) = std::env::var("COVEN_CODE_TEST_HOME") {
48+
if !home.is_empty() {
49+
return PathBuf::from(home);
50+
}
51+
}
52+
53+
dirs::home_dir().unwrap_or_else(|| PathBuf::from("."))
54+
}
55+
4556
#[derive(Debug, Clone)]
4657
pub struct FilePlan {
4758
pub source_path: PathBuf,
@@ -654,7 +665,11 @@ mod tests {
654665
.unwrap();
655666

656667
let old_home = std::env::var("HOME").ok();
668+
let old_test_home = std::env::var("COVEN_CODE_TEST_HOME").ok();
669+
let old_userprofile = std::env::var("USERPROFILE").ok();
657670
std::env::set_var("HOME", home);
671+
std::env::set_var("COVEN_CODE_TEST_HOME", home);
672+
std::env::set_var("USERPROFILE", home);
658673

659674
let preview = build_import_preview(ImportSelection::Both).unwrap();
660675
assert!(preview.claude_md.is_some());
@@ -682,6 +697,16 @@ mod tests {
682697
} else {
683698
std::env::remove_var("HOME");
684699
}
700+
if let Some(old) = old_test_home {
701+
std::env::set_var("COVEN_CODE_TEST_HOME", old);
702+
} else {
703+
std::env::remove_var("COVEN_CODE_TEST_HOME");
704+
}
705+
if let Some(old) = old_userprofile {
706+
std::env::set_var("USERPROFILE", old);
707+
} else {
708+
std::env::remove_var("USERPROFILE");
709+
}
685710
}
686711

687712
#[test]

src-rust/crates/core/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,13 @@ pub mod config {
14711471
impl Settings {
14721472
/// The per-user configuration directory (`~/.coven-code`).
14731473
pub fn config_dir() -> PathBuf {
1474+
#[cfg(test)]
1475+
if let Ok(home) = std::env::var("COVEN_CODE_TEST_HOME") {
1476+
if !home.is_empty() {
1477+
return PathBuf::from(home).join(".coven-code");
1478+
}
1479+
}
1480+
14741481
dirs::home_dir()
14751482
.unwrap_or_else(|| PathBuf::from("."))
14761483
.join(".coven-code")
@@ -4969,6 +4976,8 @@ mod tests {
49694976
fn test_imported_anthropic_cli_token_resolves_without_coven_oauth_client() {
49704977
struct EnvRestore {
49714978
home: Option<String>,
4979+
test_home: Option<String>,
4980+
userprofile: Option<String>,
49724981
api_key: Option<String>,
49734982
client_id: Option<String>,
49744983
}
@@ -4979,6 +4988,14 @@ mod tests {
49794988
Some(value) => std::env::set_var("HOME", value),
49804989
None => std::env::remove_var("HOME"),
49814990
}
4991+
match self.test_home.take() {
4992+
Some(value) => std::env::set_var("COVEN_CODE_TEST_HOME", value),
4993+
None => std::env::remove_var("COVEN_CODE_TEST_HOME"),
4994+
}
4995+
match self.userprofile.take() {
4996+
Some(value) => std::env::set_var("USERPROFILE", value),
4997+
None => std::env::remove_var("USERPROFILE"),
4998+
}
49824999
match self.api_key.take() {
49835000
Some(value) => std::env::set_var("ANTHROPIC_API_KEY", value),
49845001
None => std::env::remove_var("ANTHROPIC_API_KEY"),
@@ -5003,10 +5020,14 @@ mod tests {
50035020
let temp_home = tempfile::tempdir().expect("temp home");
50045021
let _restore = EnvRestore {
50055022
home: std::env::var("HOME").ok(),
5023+
test_home: std::env::var("COVEN_CODE_TEST_HOME").ok(),
5024+
userprofile: std::env::var("USERPROFILE").ok(),
50065025
api_key: std::env::var("ANTHROPIC_API_KEY").ok(),
50075026
client_id: std::env::var(crate::oauth::CLIENT_ID_ENV).ok(),
50085027
};
50095028
std::env::set_var("HOME", temp_home.path());
5029+
std::env::set_var("COVEN_CODE_TEST_HOME", temp_home.path());
5030+
std::env::set_var("USERPROFILE", temp_home.path());
50105031
std::env::remove_var("ANTHROPIC_API_KEY");
50115032
std::env::remove_var(crate::oauth::CLIENT_ID_ENV);
50125033

src-rust/crates/core/src/roster_reset.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,25 @@ mod tests {
139139

140140
struct HomeGuard {
141141
old_home: Option<String>,
142+
old_test_home: Option<String>,
143+
old_userprofile: Option<String>,
142144
old_coven_home: Option<String>,
143145
}
144146

145147
impl HomeGuard {
146148
fn set(home: &Path, coven_home: &Path) -> Self {
147149
let old_home = std::env::var("HOME").ok();
150+
let old_test_home = std::env::var("COVEN_CODE_TEST_HOME").ok();
151+
let old_userprofile = std::env::var("USERPROFILE").ok();
148152
let old_coven_home = std::env::var("COVEN_HOME").ok();
149153
std::env::set_var("HOME", home);
154+
std::env::set_var("COVEN_CODE_TEST_HOME", home);
155+
std::env::set_var("USERPROFILE", home);
150156
std::env::set_var("COVEN_HOME", coven_home);
151157
Self {
152158
old_home,
159+
old_test_home,
160+
old_userprofile,
153161
old_coven_home,
154162
}
155163
}
@@ -161,6 +169,14 @@ mod tests {
161169
Some(value) => std::env::set_var("HOME", value),
162170
None => std::env::remove_var("HOME"),
163171
}
172+
match &self.old_test_home {
173+
Some(value) => std::env::set_var("COVEN_CODE_TEST_HOME", value),
174+
None => std::env::remove_var("COVEN_CODE_TEST_HOME"),
175+
}
176+
match &self.old_userprofile {
177+
Some(value) => std::env::set_var("USERPROFILE", value),
178+
None => std::env::remove_var("USERPROFILE"),
179+
}
164180
match &self.old_coven_home {
165181
Some(value) => std::env::set_var("COVEN_HOME", value),
166182
None => std::env::remove_var("COVEN_HOME"),
@@ -204,12 +220,12 @@ mod tests {
204220
let home = temp.path().join("home");
205221
let coven_home = temp.path().join("coven");
206222
let project = temp.path().join("project");
207-
let global_agents = home.join(".coven-code").join("agents");
208223
let project_agents = project.join(".coven-code").join("agents");
209-
std::fs::create_dir_all(&global_agents).expect("global agents dir");
210224
std::fs::create_dir_all(&project_agents).expect("project agents dir");
211225
std::fs::create_dir_all(&coven_home).expect("coven home");
212226
let _guard = HomeGuard::set(&home, &coven_home);
227+
let global_agents = Settings::config_dir().join("agents");
228+
std::fs::create_dir_all(&global_agents).expect("global agents dir");
213229

214230
std::fs::write(global_agents.join("global.md"), "global").expect("global agent");
215231
std::fs::write(global_agents.join("README.txt"), "keep").expect("global keep");

src-rust/crates/tui/src/app.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7420,22 +7420,22 @@ role = "Research"
74207420

74217421
#[test]
74227422
fn windows_system_root_prefers_absolute_system_root() {
7423-
let root = windows_system_root_from_env(
7424-
Some(std::ffi::OsString::from("/windows")),
7425-
Some(std::ffi::OsString::from("/windir")),
7426-
);
7423+
let system_root = std::env::temp_dir().join("windows-root").into_os_string();
7424+
let windir = std::env::temp_dir().join("windir").into_os_string();
7425+
let root = windows_system_root_from_env(Some(system_root.clone()), Some(windir));
74277426

7428-
assert_eq!(root, std::ffi::OsString::from("/windows"));
7427+
assert_eq!(root, system_root);
74297428
}
74307429

74317430
#[test]
74327431
fn windows_system_root_falls_back_to_absolute_windir() {
7432+
let windir = std::env::temp_dir().join("windir").into_os_string();
74337433
let root = windows_system_root_from_env(
74347434
Some(std::ffi::OsString::from("relative-system-root")),
7435-
Some(std::ffi::OsString::from("/windir")),
7435+
Some(windir.clone()),
74367436
);
74377437

7438-
assert_eq!(root, std::ffi::OsString::from("/windir"));
7438+
assert_eq!(root, windir);
74397439
}
74407440

74417441
#[test]

0 commit comments

Comments
 (0)