1#![deny(unsafe_code)]
8
9use std::sync::OnceLock;
10
11pub const REMOTE_PROCESS_IDENTITY_SCRIPT: &str = include_str!("remote_process.sh");
13
14pub mod admission_recommendation;
15pub mod admission_rejection;
16pub mod admit_preflight;
17pub mod api;
18pub mod artifact_cost;
19pub mod artifact_pattern;
20pub mod artifact_verify;
21pub mod binary_hash;
22pub mod bypass_record;
23pub mod bypass_recovery;
24pub mod capability_probe;
25pub mod cargo_path_deps;
26pub mod classifier_drift;
27pub mod closure_explain;
28pub mod config;
29pub mod dependency_closure_planner;
30pub mod discovery;
31pub mod disk_pressure_report;
32pub mod disk_reclaim;
33pub mod e2e;
34pub mod errors;
35pub mod exec_misuse;
36pub mod exec_policy;
37pub mod exec_response;
38pub mod fleet_diff;
39pub mod fleet_provenance;
40pub mod fleet_smoke_profile;
41pub mod fleet_status;
42pub mod force_resync;
43pub mod gc_roots;
44pub mod hooks;
45pub mod incident;
46pub mod incident_ledger;
47pub mod isolated_staging;
48pub mod job_identity;
49pub mod job_recovery;
50pub mod log_retention;
51pub mod logging;
52pub mod mock;
53pub mod mock_worker;
54pub mod orphan_quarantine;
55pub mod path_topology;
56pub mod patterns;
57#[cfg(test)]
58mod patterns_security_test;
59pub mod placement;
60pub mod pooled_target_key;
61pub mod process_identity;
62pub mod proof_handoff;
63pub mod proof_intent;
64pub mod proof_policy;
65pub mod proof_replay;
66#[cfg(test)]
67mod proptest_tests;
68pub mod protocol;
69pub mod queue_contract;
70pub mod readiness;
71pub mod redaction;
72pub mod remediation_config;
73pub mod remediation_view;
74#[cfg(unix)]
75pub mod remote_compilation;
76#[cfg(unix)]
77pub mod remote_verification;
78pub mod repo_updater_contract;
79pub mod rsync_flavor;
80pub mod schema_versions;
81#[cfg(unix)]
82pub mod ssh;
83#[cfg(all(test, unix))]
84mod ssh_timeout_test;
85pub mod ssh_utils;
86pub mod stale_target_reap;
87pub mod storm_control;
88pub mod telemetry_explain;
89pub mod telemetry_freshness;
90pub mod test_change;
91pub mod testing;
92pub mod toolchain;
93pub mod transfer_hardening;
94pub mod types;
95pub mod ui;
96pub mod util;
97pub mod worker_config_validation;
98pub mod worker_facts;
99
100pub const BUILD_COMMIT_ENV_VARS: &[&str] = &[
101 "RCH_GIT_COMMIT",
102 "VERGEN_GIT_SHA",
103 "GIT_COMMIT",
104 "GITHUB_SHA",
105];
106
107pub fn build_commit() -> Option<&'static str> {
113 first_build_commit([
114 option_env!("RCH_GIT_COMMIT"),
115 option_env!("VERGEN_GIT_SHA"),
116 option_env!("GIT_COMMIT"),
117 option_env!("GITHUB_SHA"),
118 ])
119}
120
121fn first_build_commit<'a>(
122 candidates: impl IntoIterator<Item = Option<&'a str>>,
123) -> Option<&'a str> {
124 candidates
125 .into_iter()
126 .flatten()
127 .map(str::trim)
128 .find(|value| is_build_commit_stamp(value))
129}
130
131fn is_build_commit_stamp(value: &str) -> bool {
134 fn is_commit_hash(value: &str) -> bool {
135 (7..=64).contains(&value.len()) && value.bytes().all(|byte| byte.is_ascii_hexdigit())
136 }
137 is_commit_hash(value)
138 || value.strip_suffix("-dirty").is_some_and(is_commit_hash)
139 || value
140 .split_once("-overlay-")
141 .is_some_and(|(commit, fingerprint)| {
142 is_commit_hash(commit)
143 && fingerprint.len() == 64
144 && fingerprint.bytes().all(|byte| byte.is_ascii_hexdigit())
145 })
146}
147
148pub fn build_version_value() -> String {
149 build_version_value_with_commit(env!("CARGO_PKG_VERSION"), build_commit())
150}
151
152pub fn build_version_value_static() -> &'static str {
153 static VERSION_VALUE: OnceLock<String> = OnceLock::new();
154
155 VERSION_VALUE.get_or_init(build_version_value).as_str()
156}
157
158pub fn build_version_value_with_commit(package_version: &str, commit: Option<&str>) -> String {
159 let mut value = package_version.to_string();
160
161 if let Some(commit) = commit.map(str::trim).filter(|value| !value.is_empty()) {
162 value.push_str(" (commit ");
163 let (revision, suffix) = commit
164 .split_once('-')
165 .map_or((commit, None), |(revision, suffix)| {
166 (revision, Some(suffix))
167 });
168 value.push_str(short_commit(revision));
169 if let Some(suffix) = suffix {
170 value.push('-');
171 value.push_str(suffix);
172 }
173 value.push(')');
174 }
175
176 value
177}
178
179fn short_commit(commit: &str) -> &str {
180 commit
181 .char_indices()
182 .nth(12)
183 .map_or(commit, |(index, _)| &commit[..index])
184}
185
186pub use artifact_cost::{
187 ArtifactCostReport, ArtifactPhaseTimings, GlobAdvice, RetrievalMode, assess_glob_expansion,
188 compute_artifact_cost, is_broad_recursive_glob,
189};
190pub use artifact_pattern::{
191 ARTIFACT_OUTPUT_ROOTS, ArtifactPatternRewrite, ArtifactRetrievalDiagnostics,
192 artifact_dest_is_safe, rewrite_artifact_pattern,
193};
194pub use artifact_verify::{
195 ArtifactManifest, FileHash, VerificationFailure, VerificationResult, compute_file_hash,
196 create_manifest, verify_artifacts,
197};
198pub use binary_hash::{
199 BinaryHashResult, binaries_equivalent, binary_contains_marker, compute_binary_hash,
200};
201pub use bypass_record::{
202 AutoRejoinCriteria, BypassBackoff, BypassFailureClass, BypassRecord, BypassRecordStore,
203 BypassState, DisabledMigration, DisabledWorkerSnapshot, MAX_DIAGNOSTIC_CHARS,
204 bypass_record_schema, bypass_record_schema_version, classify_disable_reason,
205 default_bypass_record_path, migrate_disabled_worker, truncate_diagnostic,
206};
207pub use bypass_recovery::{
208 CanaryDecision, CanaryOutcome, ProbeDecision, RecoveryProbe, decide_canary, decide_probe,
209};
210pub use cargo_path_deps::{
211 CargoPathDependencyEdge, CargoPathDependencyError, CargoPathDependencyErrorKind,
212 CargoPathDependencyGraph, CargoPathDependencyPackage, CargoPathMaterializationClosure,
213 resolve_cargo_path_dependency_graph, resolve_cargo_path_dependency_graph_with_policy,
214 resolve_cargo_path_materialization_closure,
215 resolve_cargo_path_materialization_closure_with_policy,
216};
217pub use closure_explain::{
218 ClosureExplainEntry, ClosureExplainReport, RootConvergence, RootSyncOutcome, derive_outcome,
219 explain_closure,
220};
221pub use dependency_closure_planner::{
222 DependencyClosurePlan, DependencyClosurePlanState, DependencyPlanIssue, DependencyRiskClass,
223 DependencySyncAction, DependencySyncMetadata, DependencySyncReason,
224 build_dependency_closure_plan, build_dependency_closure_plan_with_policy,
225 plan_dependency_closure_from_graph,
226};
227pub use force_resync::{
228 ForceResyncPlan, ForceResyncReport, InvalidationAction, RefusedInvalidation, ResyncOutcome,
229 StaleRoot, apply_force_resync, is_safe_invalidation_target, plan_force_resync,
230};
231pub use gc_roots::{
232 RCH_CARGO_CACHE_PREFIX, RCH_CARGO_HOME_BASE_VAR, RCH_CARGO_HOME_PREFIX,
233 remote_cargo_cache_expr, remote_cargo_home_base_prelude, remote_cargo_home_expr,
234};
235pub use logging::{LogConfig, LogFormat, LoggingGuards, init_logging, init_logging_with_layer};
236pub use mock_worker::MockWorkerServer;
237pub use path_topology::{
238 DEFAULT_ALIAS_PROJECT_ROOT, DEFAULT_CANONICAL_PROJECT_ROOT, NormalizationDecision,
239 NormalizedProjectPath, PathNormalizationError, PathNormalizationErrorKind, PathTopologyPolicy,
240 normalize_project_path, normalize_project_path_with_policy,
241};
242pub use patterns::{
243 Classification, ClassificationDetails, ClassificationTier, CompilationKind, TierDecision,
244 classify_command, classify_command_detailed, compound_local_compilation_segments,
245 declined_compilation_due_to_structure, split_shell_commands,
246};
247pub use protocol::{HookInput, HookOutput, ToolInput};
248pub use queue_contract::{
249 AdmissionState, QueueContractOutcome, QueueContractResponse, QueueOptions, StartState,
250 WaitResult, resolve_queue_contract,
251};
252pub use repo_updater_contract::{
253 MockRepoUpdaterAdapter, REPO_UPDATER_ALIAS_PROJECTS_ROOT, REPO_UPDATER_CANONICAL_PROJECTS_ROOT,
254 REPO_UPDATER_CONTRACT_SCHEMA_VERSION, REPO_UPDATER_DEFAULT_BINARY,
255 REPO_UPDATER_MIN_SUPPORTED_VERSION, RepoUpdaterAdapter, RepoUpdaterAdapterCommand,
256 RepoUpdaterAdapterContract, RepoUpdaterAdapterRequest, RepoUpdaterAdapterResponse,
257 RepoUpdaterFailure, RepoUpdaterFailureKind, RepoUpdaterFallbackMode,
258 RepoUpdaterIdempotencyGuarantee, RepoUpdaterJsonEnvelope, RepoUpdaterOutputFormat,
259 RepoUpdaterResponseStatus, RepoUpdaterVersionCompatibility, RepoUpdaterVersionPolicy,
260 build_invocation, classify_exit_code, evaluate_version_compatibility,
261 map_failure_kind_to_error_code, repo_updater_envelope_schema, repo_updater_request_schema,
262 repo_updater_response_schema,
263};
264pub use ssh_utils::{
266 CommandResult, EnvPrefix, build_env_prefix, is_retryable_transport_error,
267 is_retryable_transport_error_text, shell_escape_value,
268};
269#[cfg(unix)]
271pub use ssh::{KnownHostsPolicy, SshClient, SshOptions, SshPool};
272pub use test_change::{TestChangeGuard, TestCodeChange};
273pub use toolchain::{ToolchainInfo, wrap_command_with_color, wrap_command_with_toolchain};
274pub use transfer_hardening::{
275 FirstFailure, RetryDecision, RsyncFailureClass, classify_rsync_outcome, is_ephemeral_path,
276};
277pub use types::{
278 AffinityConfig, ApiConfig, BoxRole, BuildCancellationMetadata, BuildCancellationWorkerHealth,
279 BuildHeartbeatPhase, BuildHeartbeatRequest, BuildLocation, BuildRecord, BuildStats,
280 CircuitBreakerConfig, CircuitState, CircuitStats, ColorMode, CommandPriority,
281 CommandTimingBreakdown, CompilationConfig, CompilationMetrics, CompilationTimer,
282 CompilationTimingBreakdown, DashboardConfig, DoctorConfig, DoctorWebhookEndpoint,
283 DoctorWebhookFormat, DoctorWebhooksConfig, EnvironmentConfig, ExecutionConfig, FairnessConfig,
284 FleetConfig, GeneralConfig, MetricsAggregator, OS_TAG_PREFIX, OutputConfig, OutputVisibility,
285 PathTopologyConfig, RchConfig, ReleaseRequest, RemoteBuildJobs, RequiredRuntime, RetryConfig,
286 SELECTION_RESPONSE_PROTOCOL_VERSION, SavedTimeStats, SelectedWorker, SelectionConfig,
287 SelectionDiagnostics, SelectionReason, SelectionRequest, SelectionResponse, SelectionStrategy,
288 SelectionWeightConfig, SelfHealingConfig, SelfHealingLogLevel, SelfTestConfig,
289 SelfTestFailureAction, SelfTestWorkers, TransferConfig, WorkerCapabilities, WorkerConfig,
290 WorkerId, WorkerSelectionDiagnostic, WorkerSelectionDiagnosticDecision, WorkerStatus,
291 declared_os, default_socket_path, os_tag, validate_remote_base,
292 x86_64_microarch_level_from_flags,
293};
294
295pub use testing::{TestLogEntry, TestLogger, TestPhase, TestResult};
297
298pub use config::{
300 ConfigSource, ConfigValueSource, ConfigWarning, EnvError, EnvParser, Profile, Severity,
301 Sourced, validate_config,
302};
303
304pub use discovery::{
306 DiscoveredHost, DiscoverySource, discover_all, parse_shell_aliases,
307 parse_shell_aliases_content, parse_ssh_config, parse_ssh_config_content,
308};
309
310pub use ui::{
312 ErrorPanel, ErrorSeverity, Icons, IntoErrorPanel, OutputContext, RchTheme, ResultExt,
313 anyhow_to_json, anyhow_to_panel, display_anyhow_error, display_error, display_error_with_code,
314 error_to_json, error_to_panel,
315};
316
317pub use capability_probe::{
319 CapabilityRequirement, CapabilityVerdict, EligibilityVerdict, ProbeSpec, ProbedFacts,
320 WorkerLiveness, assess_admissibility, assess_worker_eligibility, build_capability_probe_script,
321 parse_capability_probe, remote_worker_binary_path,
322};
323pub use incident::{
324 ControlState, IncidentEvent, IncidentEventType, IncidentReasonCode, IncidentSource,
325 SelectedMode, incident_schema_version,
326};
327pub use incident_ledger::{
328 IncidentFilter, IncidentLedger, IncidentLedgerConfig, LedgerReadStats, default_ledger_path,
329};
330pub use placement::{
331 ControlDiagnostic, ControlDiagnosticLevel, ControlKind, PlacementControl, PlacementPlan,
332 QueuePolicy, RequestedWorkerFacts, RequestedWorkerOutcome, RequestedWorkerStatus,
333 StrictRemotePolicy, TargetDirPolicy, VisibilityMode, evaluate_requested_worker,
334 placement_controls, resolve_placement,
335};
336pub use proof_intent::{
337 ProofIntent, ProofIntentStore, ReplayConstraints, ReplayContext, ReplayDecision,
338 SourceFingerprint, StaleSourcePolicy, derive_intent_id, proof_intent_schema,
339 proof_intent_schema_version, validate_replay,
340};
341pub use telemetry_explain::{
342 ProbeOutcome, TelemetrySignals, TelemetryUnavailabilityReason, WhyUnhealthy,
343 explain_unavailability,
344};
345pub use telemetry_freshness::{
346 FreshnessAssessment, FreshnessInputs, FreshnessVerdict, assess as assess_telemetry_freshness,
347};
348pub use worker_config_validation::{
349 ConfigDriftFinding, ConfigDriftKind, LiveHostObservation, validate_worker_config,
350};
351pub use worker_facts::{
352 DiskFacts, DiskRootFacts, HostFacts, RuntimeFacts, RustFacts, UserFacts, WorkerBinaryFacts,
353 WorkerFacts, derive_target_triple, worker_facts_schema, worker_facts_schema_version,
354};
355
356pub use errors::{
358 CodeExplanation, CodeNamespace, ErrorCategory, ErrorCode, ErrorEntry, ReliabilityCategoryKind,
359 ReliabilityReasonCode, RunbookEntry,
360};
361
362pub use schema_versions::current_version as schema_version;
364pub use schema_versions::{ALL_COMPONENTS as SCHEMA_VERSION_COMPONENTS, SchemaComponent};
365
366pub use api::{API_VERSION, ApiError, ApiResponse, ErrorContext, LegacyErrorCode};
368
369pub use hooks::{HookResult, is_claude_code_installed, verify_and_install_claude_code_hook};
371
372#[cfg(test)]
373mod build_version_tests {
374 use super::{BUILD_COMMIT_ENV_VARS, build_version_value_with_commit, first_build_commit};
375
376 #[test]
377 fn build_commit_skips_values_the_build_script_rejects() {
378 assert_eq!(
380 first_build_commit([None, None, Some("not-a-commit"), None]),
381 None
382 );
383 let hash = "0123456789abcdef0123456789abcdef01234567";
384 assert_eq!(
385 first_build_commit([Some(""), None, Some("v2.0.0-3-gdeadbee"), Some(hash)]),
386 Some(hash)
387 );
388 assert_eq!(first_build_commit([Some(" abc1234 ")]), Some("abc1234"));
389 assert_eq!(first_build_commit([Some("abc123")]), None, "too short");
390 let dirty = format!("{hash}-dirty");
391 assert_eq!(
392 first_build_commit([Some(dirty.as_str())]),
393 Some(dirty.as_str())
394 );
395 let overlay = format!("{hash}-overlay-{}", "b".repeat(64));
396 assert_eq!(
397 first_build_commit([Some(overlay.as_str())]),
398 Some(overlay.as_str())
399 );
400 let short_overlay = format!("{hash}-overlay-{}", "b".repeat(63));
401 assert_eq!(first_build_commit([Some(short_overlay.as_str())]), None);
402 }
403
404 #[test]
405 fn build_version_value_omits_missing_commit() {
406 assert_eq!(build_version_value_with_commit("1.0.24", None), "1.0.24");
407 }
408
409 #[test]
410 fn build_version_value_trims_and_shortens_commit() {
411 assert_eq!(
412 build_version_value_with_commit(
413 "1.0.24",
414 Some(" 2fa1249a18dfd05ae5e319e8d10bfd3c9ea1af55 "),
415 ),
416 "1.0.24 (commit 2fa1249a18df)"
417 );
418 }
419
420 #[test]
421 fn build_version_value_ignores_empty_commit() {
422 assert_eq!(
423 build_version_value_with_commit("1.0.24", Some(" ")),
424 "1.0.24"
425 );
426 }
427
428 #[test]
429 fn build_source_stamp_version_preserves_nonclean_suffixes() {
430 let hash = "a".repeat(40);
431 for suffix in ["dirty".to_string(), format!("overlay-{}", "b".repeat(64))] {
432 assert_eq!(
433 build_version_value_with_commit("2.0.0", Some(&format!("{hash}-{suffix}"))),
434 format!("2.0.0 (commit {}-{suffix})", "a".repeat(12))
435 );
436 }
437 }
438
439 #[test]
440 fn build_commit_env_var_order_is_documented() {
441 assert_eq!(
442 BUILD_COMMIT_ENV_VARS,
443 &[
444 "RCH_GIT_COMMIT",
445 "VERGEN_GIT_SHA",
446 "GIT_COMMIT",
447 "GITHUB_SHA"
448 ]
449 );
450 }
451}