Skip to content

Commit 034179a

Browse files
committed
fix(ci): unblock main + tighten rust-cache keying
- crab-fs glob: use sort_by_key to satisfy clippy::unnecessary_sort_by - crab-common ca_certs: inject env sources in tests so ubuntu runner's SSL_CERT_FILE (system bundle) doesn't pollute assertions - deny.toml: allow MIT-0 (borrow-or-share via fluent-uri, OSI approved, strictly more permissive than MIT) - ci.yml: share cache key between check + test-ubuntu jobs, disable CARGO_INCREMENTAL, trim debuginfo, prefer git CLI for fetches
1 parent c395e33 commit 034179a

File tree

4 files changed

+66
-17
lines changed

4 files changed

+66
-17
lines changed

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ on:
88
env:
99
CARGO_TERM_COLOR: always
1010
RUSTFLAGS: -Dwarnings
11+
# Disable incremental compilation on CI: unused by fresh builds and
12+
# wastes cache space. See https://matklad.github.io/2021/09/04/fast-rust-builds.html
13+
CARGO_INCREMENTAL: 0
14+
# Use git CLI for cargo fetches — more reliable on Windows than libgit2.
15+
CARGO_NET_GIT_FETCH_WITH_CLI: true
16+
# Shrink debuginfo to speed compile + cache. Tests still run fine.
17+
CARGO_PROFILE_DEV_DEBUG: "line-tables-only"
18+
CARGO_PROFILE_TEST_DEBUG: "line-tables-only"
1119

1220
jobs:
1321
check:
@@ -20,6 +28,14 @@ jobs:
2028
toolchain: stable
2129
components: rustfmt, clippy
2230
- uses: Swatinem/rust-cache@v2
31+
with:
32+
# Share cache between check & test on ubuntu — same toolchain
33+
# + Cargo.lock → same dependency graph, so this is safe and
34+
# roughly halves cold-start time for the test-ubuntu job.
35+
shared-key: "ubuntu-stable"
36+
# Save cache even if a step fails. Keeps warm cache primed so
37+
# the next run (after fix) can benefit.
38+
save-if: ${{ github.ref == 'refs/heads/main' }}
2339
- run: cargo fmt --all --check
2440
- run: cargo clippy --workspace -- -D warnings
2541
- run: cargo check --workspace
@@ -39,6 +55,11 @@ jobs:
3955
with:
4056
toolchain: stable
4157
- uses: Swatinem/rust-cache@v2
58+
with:
59+
# Ubuntu shares the check job's cache (same shared-key); win/mac
60+
# get their own per-OS cache (rust-cache keys on OS automatically).
61+
shared-key: ${{ matrix.os == 'ubuntu-latest' && 'ubuntu-stable' || '' }}
62+
save-if: ${{ github.ref == 'refs/heads/main' }}
4263
- uses: taiki-e/install-action@nextest
4364
continue-on-error: true
4465
id: nextest

crates/common/src/utils/ca_certs.rs

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,40 @@ impl CaBundle {
8080
/// Returns `Err` only on invalid PEM data in a file that was otherwise
8181
/// readable. Missing files / missing env vars are not errors.
8282
pub fn load_ca_bundle(extra_paths: &[PathBuf]) -> crate::Result<CaBundle> {
83+
let env = EnvSources {
84+
crab_bundle: std::env::var(CRAB_CA_BUNDLE_ENV).ok(),
85+
ssl_cert_file: std::env::var(SSL_CERT_FILE_ENV).ok(),
86+
ssl_cert_dir: std::env::var(SSL_CERT_DIR_ENV).ok(),
87+
};
88+
load_ca_bundle_with_env(&env, extra_paths)
89+
}
90+
91+
/// Env-var inputs to the CA loader.
92+
///
93+
/// Split out from [`load_ca_bundle`] so tests can inject a hermetic
94+
/// environment instead of reading the process's real env (which on CI
95+
/// runners includes system-wide cert bundles that pollute assertions).
96+
#[derive(Debug, Default, Clone)]
97+
struct EnvSources {
98+
crab_bundle: Option<String>,
99+
ssl_cert_file: Option<String>,
100+
ssl_cert_dir: Option<String>,
101+
}
102+
103+
fn load_ca_bundle_with_env(env: &EnvSources, extra_paths: &[PathBuf]) -> crate::Result<CaBundle> {
83104
let mut bundle = CaBundle::default();
84105

85106
// 1. CRAB_CA_BUNDLE (single file, takes priority)
86-
if let Ok(path) = std::env::var(CRAB_CA_BUNDLE_ENV) {
87-
try_load_file(&mut bundle, Path::new(&path));
107+
if let Some(path) = env.crab_bundle.as_deref() {
108+
try_load_file(&mut bundle, Path::new(path));
88109
}
89110
// 2. SSL_CERT_FILE
90-
if let Ok(path) = std::env::var(SSL_CERT_FILE_ENV) {
91-
try_load_file(&mut bundle, Path::new(&path));
111+
if let Some(path) = env.ssl_cert_file.as_deref() {
112+
try_load_file(&mut bundle, Path::new(path));
92113
}
93114
// 3. SSL_CERT_DIR
94-
if let Ok(dir) = std::env::var(SSL_CERT_DIR_ENV) {
95-
try_load_dir(&mut bundle, Path::new(&dir));
115+
if let Some(dir) = env.ssl_cert_dir.as_deref() {
116+
try_load_dir(&mut bundle, Path::new(dir));
96117
}
97118
// 4. Caller extras
98119
for path in extra_paths {
@@ -256,13 +277,19 @@ mod tests {
256277
);
257278
}
258279

280+
/// Hermetic load: no env vars, only explicit paths. Used by tests to
281+
/// avoid picking up the CI runner's system cert bundle (e.g. Ubuntu
282+
/// sets `SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt`, which
283+
/// would add hundreds of certs and break `assert_eq!(b.len(), N)`).
284+
fn load_hermetic(extra_paths: &[PathBuf]) -> CaBundle {
285+
load_ca_bundle_with_env(&EnvSources::default(), extra_paths).unwrap()
286+
}
287+
259288
#[test]
260289
fn load_missing_env_returns_empty_bundle() {
261-
// None of the env vars are set in this test; nothing loaded.
262-
let b = load_ca_bundle(&[]).unwrap();
263-
// May not be empty if the user's env already has SSL_CERT_FILE set,
264-
// so check the weaker property: no-extra-paths load succeeds.
265-
let _ = b;
290+
let b = load_hermetic(&[]);
291+
assert!(b.is_empty());
292+
assert_eq!(b.len(), 0);
266293
}
267294

268295
#[test]
@@ -272,7 +299,7 @@ mod tests {
272299
let mut f = fs::File::create(&path).unwrap();
273300
f.write_all(CERT_A.as_bytes()).unwrap();
274301

275-
let b = load_ca_bundle(std::slice::from_ref(&path)).unwrap();
302+
let b = load_hermetic(std::slice::from_ref(&path));
276303
assert_eq!(b.len(), 1);
277304
assert_eq!(b.sources, vec![path]);
278305
}
@@ -284,15 +311,15 @@ mod tests {
284311
fs::write(tmp.path().join("b.crt"), CERT_B).unwrap();
285312
fs::write(tmp.path().join("readme.txt"), "ignored").unwrap();
286313

287-
let b = load_ca_bundle(std::slice::from_ref(&tmp.path().to_path_buf())).unwrap();
314+
let b = load_hermetic(std::slice::from_ref(&tmp.path().to_path_buf()));
288315
assert_eq!(b.len(), 2);
289316
assert_eq!(b.sources.len(), 2);
290317
}
291318

292319
#[test]
293320
fn load_extra_path_nonexistent_is_silent() {
294-
let b = load_ca_bundle(&[PathBuf::from("/definitely/does/not/exist/bundle.pem")]).unwrap();
295-
let _ = b;
321+
let b = load_hermetic(&[PathBuf::from("/definitely/does/not/exist/bundle.pem")]);
322+
assert!(b.is_empty());
296323
}
297324

298325
#[test]
@@ -301,7 +328,7 @@ mod tests {
301328
let path = tmp.path().join("empty.pem");
302329
fs::write(&path, "just some text, no BEGIN CERTIFICATE").unwrap();
303330

304-
let b = load_ca_bundle(std::slice::from_ref(&path)).unwrap();
331+
let b = load_hermetic(std::slice::from_ref(&path));
305332
// The file was readable but had no valid blocks; bundle stays empty.
306333
assert_eq!(b.len(), 0);
307334
}

crates/fs/src/glob.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub fn find_files(opts: &GlobOptions<'_>) -> crab_common::Result<GlobResult> {
111111
}
112112

113113
// 4. Sort by mtime descending (most recent first)
114-
entries.sort_by(|a, b| b.1.cmp(&a.1));
114+
entries.sort_by_key(|e| std::cmp::Reverse(e.1));
115115

116116
// 5. Truncate at limit
117117
let truncated = opts.limit > 0 && entries.len() > opts.limit;

deny.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ allow = [
1010
"Apache-2.0",
1111
"Apache-2.0 WITH LLVM-exception",
1212
"MIT",
13+
"MIT-0",
1314
"BSD-2-Clause",
1415
"BSD-3-Clause",
1516
"ISC",

0 commit comments

Comments
 (0)