Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .alfonso/release-notes/next/runon-macos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bash can now send build and test commands to a macOS machine with runon: "macos" (fixed size, offline), so macOS-only tests no longer have to run on the local machine.
8 changes: 8 additions & 0 deletions benchmarks/aft-search/slice-descriptors/land-runon-macos.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"targeted_mechanism": "none",
"kind": "harness",
"slice_class": "non_ranking",
"fixtures": [
"harness-goldens"
]
}
27 changes: 25 additions & 2 deletions crates/aft/src/bash_background/remote_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5737,6 +5737,26 @@ async fn runon_sends_the_whole_compound_line_remote_exactly_as_written() {
);
}

#[tokio::test]
async fn runon_macos_reaches_the_wire_request_as_macos_platform() {
let daemon = daemon(Script::Plain, "exec-remote/v1").await;
let dir = tempfile::tempdir().unwrap();
let ctx = restarted_context(dir.path());
let response = handle_with_policy(
&ctx,
Some(launch(daemon.connection.clone())),
serde_json::json!({"command": "uname -a", "runon": "macos", "compressed": false}),
);
assert!(response.success, "{response:?}");
let task_id = response.data["task_id"].as_str().unwrap().to_string();
let _done = terminal(ctx.bash_background(), &task_id).await;
let runs = exec_runs(&daemon);
assert_eq!(runs.len(), 1, "{runs:?}");
assert_eq!(runs[0]["platform"], "macos");
assert!(runs[0].get("weight_hint").is_none(), "{runs:?}");
assert!(runs[0].get("network").is_none(), "{runs:?}");
}

#[tokio::test]
async fn runon_vcpu_count_is_sent_as_weight_hint_on_the_wire() {
let daemon = daemon(Script::Plain, "exec-remote/v1").await;
Expand Down Expand Up @@ -5798,6 +5818,9 @@ async fn malformed_runon_vcpu_requests_are_refused_before_dispatch() {
"windows,4c",
"windows,net",
"windows,wat",
"macos,4c",
"macos,net",
"macos,wat",
] {
let response = handle_with_policy(
&restarted_context(dir.path()),
Expand All @@ -5807,9 +5830,9 @@ async fn malformed_runon_vcpu_requests_are_refused_before_dispatch() {
assert!(!response.success, "{runon}: {response:?}");
let message = response.data["message"].as_str().unwrap_or_default();
assert!(message.contains(runon), "{runon}: {response:?}");
let reason = if runon == "windows,4c" {
let reason = if runon == "windows,4c" || runon == "macos,4c" {
"fixed at 4 vCPUs"
} else if runon == "windows,net" {
} else if runon == "windows,net" || runon == "macos,net" {
"no network"
} else {
"not valid"
Expand Down
47 changes: 46 additions & 1 deletion crates/aft/src/exec_remote/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use serde::{Deserialize, Serialize};

/// Runner platforms whose demands `runon` accepts; options are parsed separately.
pub const KNOWN_DEMANDS: &[&str] = &["linux", "windows"];
pub const KNOWN_DEMANDS: &[&str] = &["linux", "windows", "macos"];

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
Expand Down Expand Up @@ -120,12 +120,17 @@ pub fn parse_demand(demand: &str) -> Result<ResolvedDemand, String> {
let mut resolved = ResolvedDemand::default();
if platform == "windows" {
resolved.platform = Some(super::types::Platform::Windows);
} else if platform == "macos" {
resolved.platform = Some(super::types::Platform::Macos);
}
for token in tokens {
if token == "net" {
if platform == "windows" {
return Err(format!("runon {demand:?} is not valid: Windows jobs have no network; windows,net is not supported"));
}
if platform == "macos" {
return Err(format!("runon {demand:?} is not valid: macOS jobs have no network; macos,net is not supported"));
}
if resolved.network {
return Err(format!(
"runon {demand:?} is not valid: duplicate net option"
Expand All @@ -146,6 +151,9 @@ pub fn parse_demand(demand: &str) -> Result<ResolvedDemand, String> {
if platform == "windows" {
return Err(format!("runon {demand:?} is not valid: the Windows slot is fixed at 4 vCPUs; use plain windows without a vCPU count"));
}
if platform == "macos" {
return Err(format!("runon {demand:?} is not valid: the macOS slot is fixed at 4 vCPUs; use plain macos without a vCPU count"));
}
if resolved.weight_hint.is_some() {
return Err(format!(
"runon {demand:?} is not valid: duplicate vCPU count"
Expand All @@ -160,6 +168,9 @@ fn invalid_vcpu_demand(demand: &str) -> String {
if demand.starts_with("windows,") {
return format!("runon {demand:?} is not valid: Windows jobs accept no options; use plain windows (fixed at 4 vCPUs, no network)");
}
if demand.starts_with("macos,") {
return format!("runon {demand:?} is not valid: macOS jobs accept no options; use plain macos (fixed at 4 vCPUs, no network)");
}
format!("runon {demand:?} is not valid: options are net and a vCPU count as Nc, for example \"linux,4c,net\"")
}

Expand Down Expand Up @@ -200,6 +211,40 @@ mod tests {
}
}

#[test]
fn macos_demand_accepts_only_the_fixed_offline_slot() {
let resolved = resolve_demand("macos", None).unwrap();
assert_eq!(
resolved.platform,
Some(super::super::types::Platform::Macos)
);
assert_eq!(resolved.weight_hint, None);
assert!(!resolved.network);
assert_eq!(resolve_demand("", Some("macos")).unwrap(), resolved);
for (demand, reason) in [
(
"macos,4c",
"the macOS slot is fixed at 4 vCPUs; use plain macos without a vCPU count",
),
(
"macos,net",
"macOS jobs have no network; macos,net is not supported",
),
(
"macos,4c,net",
"the macOS slot is fixed at 4 vCPUs; use plain macos without a vCPU count",
),
(
"macos,wat",
"macOS jobs accept no options; use plain macos (fixed at 4 vCPUs, no network)",
),
] {
let error = resolve_demand(demand, None).unwrap_err();
assert!(error.contains(demand), "{error}");
assert!(error.contains(reason), "{error}");
}
}

#[test]
fn network_demand_options_parse_in_any_order_and_refuse_duplicates_by_name() {
for demand in ["linux,net", "linux,4c,net", "linux,net,4c"] {
Expand Down
Loading
Loading