Skip to content

Commit

Permalink
feat: support custom GitHub runners (#614)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj authored Dec 4, 2023
1 parent 7687ae4 commit 41e79b1
Show file tree
Hide file tree
Showing 6 changed files with 625 additions and 27 deletions.
65 changes: 39 additions & 26 deletions cargo-dist/src/backend/ci/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//!
//! In the future this may get split up into submodules.
use std::collections::HashMap;

use axoasset::LocalAsset;
use cargo_dist_schema::{GithubMatrix, GithubMatrixEntry};
use serde::Serialize;
Expand Down Expand Up @@ -108,14 +110,14 @@ impl GithubCiInfo {

// Figure out what Local Artifact tasks we need
let local_runs = if dist.merge_tasks {
distribute_targets_to_runners_merged(local_targets)
distribute_targets_to_runners_merged(local_targets, &dist.github_custom_runners)
} else {
distribute_targets_to_runners_split(local_targets)
distribute_targets_to_runners_split(local_targets, &dist.github_custom_runners)
};
for (runner, targets) in local_runs {
use std::fmt::Write;
let install_dist =
install_dist_for_github_runner(runner, &install_dist_sh, &install_dist_ps1);
install_dist_for_targets(&targets, &install_dist_sh, &install_dist_ps1);
let mut dist_args = String::from("--artifacts=local");
for target in &targets {
write!(dist_args, " --target={target}").unwrap();
Expand Down Expand Up @@ -192,16 +194,17 @@ impl GithubCiInfo {
/// succeed (uploading itself to the draft release).
///
/// In priniciple it does remove some duplicated setup work, so this is ostensibly "cheaper".
fn distribute_targets_to_runners_merged(
targets: SortedSet<&TargetTriple>,
) -> std::vec::IntoIter<(GithubRunner, Vec<&TargetTriple>)> {
fn distribute_targets_to_runners_merged<'a>(
targets: SortedSet<&'a TargetTriple>,
custom_runners: &HashMap<String, String>,
) -> std::vec::IntoIter<(GithubRunner, Vec<&'a TargetTriple>)> {
let mut groups = SortedMap::<GithubRunner, Vec<&TargetTriple>>::new();
for target in targets {
let runner = github_runner_for_target(target);
let runner = github_runner_for_target(target, custom_runners);
let runner = runner.unwrap_or_else(|| {
let default = GITHUB_LINUX_RUNNER;
warn!("not sure which github runner should be used for {target}, assuming {default}");
default
default.to_owned()
});
groups.entry(runner).or_default().push(target);
}
Expand All @@ -212,24 +215,25 @@ fn distribute_targets_to_runners_merged(

/// Given a set of targets we want to build local artifacts for, map them to Github Runners
/// while preferring each target gets its own runner for latency and fault-isolation.
fn distribute_targets_to_runners_split(
targets: SortedSet<&TargetTriple>,
) -> std::vec::IntoIter<(GithubRunner, Vec<&TargetTriple>)> {
fn distribute_targets_to_runners_split<'a>(
targets: SortedSet<&'a TargetTriple>,
custom_runners: &HashMap<String, String>,
) -> std::vec::IntoIter<(GithubRunner, Vec<&'a TargetTriple>)> {
let mut groups = vec![];
for target in targets {
let runner = github_runner_for_target(target);
let runner = github_runner_for_target(target, custom_runners);
let runner = runner.unwrap_or_else(|| {
let default = GITHUB_LINUX_RUNNER;
warn!("not sure which github runner should be used for {target}, assuming {default}");
default
default.to_owned()
});
groups.push((runner, vec![target]));
}
groups.into_iter()
}

/// A string representing a Github Runner
type GithubRunner = &'static str;
type GithubRunner = String;
/// The Github Runner to use for Linux
const GITHUB_LINUX_RUNNER: &str = "ubuntu-20.04";
/// The Github Runner to use for macos
Expand All @@ -238,34 +242,43 @@ const GITHUB_MACOS_RUNNER: &str = "macos-11";
const GITHUB_WINDOWS_RUNNER: &str = "windows-2019";

/// Get the appropriate Github Runner for building a target
fn github_runner_for_target(target: &TargetTriple) -> Option<GithubRunner> {
fn github_runner_for_target(
target: &TargetTriple,
custom_runners: &HashMap<String, String>,
) -> Option<GithubRunner> {
if let Some(runner) = custom_runners.get(target) {
return Some(runner.to_owned());
}

// We want to default to older runners to minimize the places
// where random system dependencies can creep in and be very
// recent. This helps with portability!
if target.contains("linux") {
Some(GITHUB_LINUX_RUNNER)
Some(GITHUB_LINUX_RUNNER.to_owned())
} else if target.contains("apple") {
Some(GITHUB_MACOS_RUNNER)
Some(GITHUB_MACOS_RUNNER.to_owned())
} else if target.contains("windows") {
Some(GITHUB_WINDOWS_RUNNER)
Some(GITHUB_WINDOWS_RUNNER.to_owned())
} else {
None
}
}

/// Select the cargo-dist installer approach for a given Github Runner
fn install_dist_for_github_runner<'a>(
runner: GithubRunner,
fn install_dist_for_targets<'a>(
targets: &'a [&'a TargetTriple],
install_sh: &'a str,
install_ps1: &'a str,
) -> &'a str {
if runner == GITHUB_LINUX_RUNNER || runner == GITHUB_MACOS_RUNNER {
install_sh
} else if runner == GITHUB_WINDOWS_RUNNER {
install_ps1
} else {
unreachable!("internal error: unknown github runner!?")
for target in targets {
if target.contains("linux") || target.contains("apple") {
return install_sh;
} else if target.contains("windows") {
return install_ps1;
}
}

unreachable!("internal error: unknown target triple!?")
}

fn brewfile_from(packages: &[String]) -> String {
Expand Down
11 changes: 10 additions & 1 deletion cargo-dist/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Config types (for workspace.metadata.dist)
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap};

use axoasset::{toml_edit, SourceFile};
use axoproject::{WorkspaceKind, WorkspaceSearch};
Expand Down Expand Up @@ -276,6 +276,10 @@ pub struct DistMetadata {
/// Any extra artifacts and their buildscripts
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_artifacts: Option<Vec<ExtraArtifact>>,

/// Custom GitHub runners, mapped by triple target
#[serde(skip_serializing_if = "Option::is_none")]
pub github_custom_runners: Option<HashMap<String, String>>,
}

impl DistMetadata {
Expand Down Expand Up @@ -313,6 +317,7 @@ impl DistMetadata {
msvc_crt_static: _,
hosting: _,
extra_artifacts: _,
github_custom_runners: _,
} = self;
if let Some(include) = include {
for include in include {
Expand Down Expand Up @@ -359,6 +364,7 @@ impl DistMetadata {
msvc_crt_static,
hosting,
extra_artifacts,
github_custom_runners,
} = self;

// Check for global settings on local packages
Expand Down Expand Up @@ -453,6 +459,9 @@ impl DistMetadata {
if extra_artifacts.is_none() {
*extra_artifacts = workspace_config.extra_artifacts.clone();
}
if github_custom_runners.is_none() {
*github_custom_runners = workspace_config.github_custom_runners.clone();
}

// This was historically implemented as extend, but I'm not convinced the
// inconsistency is worth the inconvenience...
Expand Down
10 changes: 10 additions & 0 deletions cargo-dist/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ fn get_new_dist_metadata(
msvc_crt_static: None,
hosting: None,
extra_artifacts: None,
github_custom_runners: None,
}
};

Expand Down Expand Up @@ -772,6 +773,7 @@ fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &DistMetadata) {
msvc_crt_static,
hosting,
extra_artifacts: _,
github_custom_runners: _,
} = &meta;

apply_optional_value(
Expand Down Expand Up @@ -965,6 +967,14 @@ fn apply_dist_to_metadata(metadata: &mut toml_edit::Item, meta: &DistMetadata) {
hosting.as_ref(),
);

// NOTE: HashMap not supported by axoasset
// apply_optional_value(
// table,
// "github-custom-runners",
// "# Custom GitHub runners to use for builds, mapped by triple target\n",
// github_custom_runners.as_ref(),
// );

// Finalize the table
table
.decor_mut()
Expand Down
8 changes: 8 additions & 0 deletions cargo-dist/src/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
//! Also note that the BuildSteps for installers are basically monolithic "build that installer"
//! steps to give them the freedom to do whatever they need to do.
use std::collections::HashMap;
use std::process::Command;

use axoproject::{PackageId, PackageIdx, WorkspaceInfo};
Expand Down Expand Up @@ -198,6 +199,8 @@ pub struct DistGraph {
pub hosting: Option<HostingInfo>,
/// Additional artifacts to build and upload
pub extra_artifacts: Vec<ExtraArtifact>,
/// Custom GitHub runners, mapped by triple target
pub github_custom_runners: HashMap<String, String>,
}

/// Info about artifacts should be hosted
Expand Down Expand Up @@ -734,6 +737,7 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
msvc_crt_static,
hosting,
extra_artifacts,
github_custom_runners: _,
} = &workspace_metadata;

let desired_cargo_dist_version = cargo_dist_version.clone();
Expand Down Expand Up @@ -846,6 +850,10 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
msvc_crt_static,
hosting,
extra_artifacts: extra_artifacts.clone().unwrap_or_default(),
github_custom_runners: workspace_metadata
.github_custom_runners
.clone()
.unwrap_or_default(),
},
manifest: DistManifest {
dist_version: Some(env!("CARGO_PKG_VERSION").to_owned()),
Expand Down
32 changes: 32 additions & 0 deletions cargo-dist/tests/integration-tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,38 @@ scope = "@axodotdev"
})
}

#[test]
fn axolotlsay_custom_github_runners() -> Result<(), miette::Report> {
let test_name = _function_name!();
AXOLOTLSAY.run_test(|ctx| {
let dist_version = ctx.tools.cargo_dist.version().unwrap();
ctx.patch_cargo_toml(format!(r#"
[workspace.metadata.dist]
cargo-dist-version = "{dist_version}"
installers = []
targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl"]
ci = ["github"]
[workspace.metadata.dist.github-custom-runners]
x86_64-unknown-linux-gnu = "buildjet-8vcpu-ubuntu-2204"
x86_64-unknown-linux-musl = "buildjet-8vcpu-ubuntu-2204"
aarch64-unknown-linux-gnu = "buildjet-8vcpu-ubuntu-2204-arm"
aarch64-unknown-linux-musl = "buildjet-8vcpu-ubuntu-2204-arm"
"#
))?;

// Run generate to make sure stuff is up to date before running other commands
let ci_result = ctx.cargo_dist_generate(test_name)?;
let ci_snap = ci_result.check_all()?;
// Do usual build+plan checks
let main_result = ctx.cargo_dist_build_and_plan(test_name)?;
let main_snap = main_result.check_all(ctx, ".cargo/bin/")?;
// snapshot all
main_snap.join(ci_snap).snap();
Ok(())
})
}

#[test]
fn akaikatana_basic() -> Result<(), miette::Report> {
let test_name = _function_name!();
Expand Down
Loading

0 comments on commit 41e79b1

Please sign in to comment.