Try gh-workflow (#41155)
Experimenting with not writing YAML by hand... Release Notes: - N/A
This commit is contained in:
@@ -10,9 +10,12 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
backtrace.workspace = true
|
||||
cargo_metadata.workspace = true
|
||||
cargo_toml.workspace = true
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
toml.workspace = true
|
||||
indoc.workspace = true
|
||||
indexmap.workspace = true
|
||||
toml_edit.workspace = true
|
||||
gh-workflow.workspace = true
|
||||
|
||||
@@ -20,6 +20,7 @@ enum CliCommand {
|
||||
PackageConformity(tasks::package_conformity::PackageConformityArgs),
|
||||
/// Publishes GPUI and its dependencies to crates.io.
|
||||
PublishGpui(tasks::publish_gpui::PublishGpuiArgs),
|
||||
Workflows(tasks::workflows::GenerateWorkflowArgs),
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -32,5 +33,6 @@ fn main() -> Result<()> {
|
||||
tasks::package_conformity::run_package_conformity(args)
|
||||
}
|
||||
CliCommand::PublishGpui(args) => tasks::publish_gpui::run_publish_gpui(args),
|
||||
CliCommand::Workflows(args) => tasks::workflows::run_workflows(args),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod clippy;
|
||||
pub mod licenses;
|
||||
pub mod package_conformity;
|
||||
pub mod publish_gpui;
|
||||
pub mod workflows;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
mod runners;
|
||||
mod steps;
|
||||
mod vars;
|
||||
mod workflows;
|
||||
|
||||
use workflows::*;
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct GenerateWorkflowArgs {}
|
||||
|
||||
pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
|
||||
let dir = Path::new(".github/workflows");
|
||||
|
||||
let workflows = vec![("danger.yml", danger()), ("nix.yml", nix())];
|
||||
fs::create_dir_all(dir)
|
||||
.with_context(|| format!("Failed to create directory: {}", dir.display()))?;
|
||||
|
||||
for (filename, workflow) in workflows {
|
||||
let content = workflow
|
||||
.to_string()
|
||||
.map_err(|e| anyhow::anyhow!("{}: {:?}", filename, e))?;
|
||||
let content = format!("# generated `cargo xtask workflows`. Do not edit.\n{content}");
|
||||
let file_path = dir.join(filename);
|
||||
fs::write(&file_path, content)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
pub const LINUX_CHEAP: Runner = Runner("namespace-profile-2x4-ubuntu-2404");
|
||||
pub const LINUX_DEFAULT: Runner = Runner("namespace-profile-16x32-ubuntu-2204");
|
||||
pub const MAC_DEFAULT: Runner = Runner("self-mini-macos");
|
||||
|
||||
pub struct Runner(&'static str);
|
||||
|
||||
impl Into<gh_workflow::RunsOn> for Runner {
|
||||
fn into(self) -> gh_workflow::RunsOn {
|
||||
self.0.into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
use gh_workflow::*;
|
||||
|
||||
pub fn checkout_repo() -> Step<Use> {
|
||||
named::uses(
|
||||
"actions",
|
||||
"checkout",
|
||||
"11bd71901bbe5b1630ceea73d27597364c9af683", // v4
|
||||
)
|
||||
}
|
||||
|
||||
pub fn setup_pnpm() -> Step<Use> {
|
||||
named::uses(
|
||||
"pnpm",
|
||||
"action-setup",
|
||||
"fe02b34f77f8bc703788d5817da081398fad5dd2", // v4.0.0
|
||||
)
|
||||
.add_with(("version", "9"))
|
||||
}
|
||||
|
||||
pub mod danger {
|
||||
use super::*;
|
||||
|
||||
pub fn setup_node() -> Step<Use> {
|
||||
named::uses(
|
||||
"actions",
|
||||
"setup-node",
|
||||
"49933ea5288caeca8642d1e84afbd3f7d6820020", // v4
|
||||
)
|
||||
.add_with(("node-version", "20"))
|
||||
.add_with(("cache", "pnpm"))
|
||||
.add_with(("cache-dependency-path", "script/danger/pnpm-lock.yaml"))
|
||||
}
|
||||
|
||||
pub fn install_deps() -> Step<Run> {
|
||||
named::run("pnpm install --dir script/danger")
|
||||
}
|
||||
|
||||
pub fn run() -> Step<Run> {
|
||||
named::run("pnpm run --dir script/danger danger ci")
|
||||
// This GitHub token is not used, but the value needs to be here to prevent
|
||||
// Danger from throwing an error.
|
||||
.add_env(("GITHUB_TOKEN", "not_a_real_token"))
|
||||
// All requests are instead proxied through an instance of
|
||||
// https://github.com/maxdeviant/danger-proxy that allows Danger to securely
|
||||
// authenticate with GitHub while still being able to run on PRs from forks.
|
||||
.add_env((
|
||||
"DANGER_GITHUB_API_BASE_URL",
|
||||
"https://danger-proxy.fly.dev/github",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub mod nix {
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::tasks::workflows::vars;
|
||||
|
||||
use super::*;
|
||||
|
||||
// on our macs we manually install nix. for some reason the cachix action is running
|
||||
// under a non-login /bin/bash shell which doesn't source the proper script to add the
|
||||
// nix profile to PATH, so we manually add them here
|
||||
pub fn set_path() -> Step<Run> {
|
||||
named::run(indoc! {r#"
|
||||
echo "/nix/var/nix/profiles/default/bin" >> "$GITHUB_PATH"
|
||||
echo "/Users/administrator/.nix-profile/bin" >> "$GITHUB_PATH"
|
||||
"#})
|
||||
}
|
||||
|
||||
pub fn install_nix() -> Step<Use> {
|
||||
named::uses(
|
||||
"cachix",
|
||||
"install-nix-action",
|
||||
"02a151ada4993995686f9ed4f1be7cfbb229e56f", // v31
|
||||
)
|
||||
.add_with(("github_access_token", vars::GITHUB_TOKEN))
|
||||
}
|
||||
|
||||
pub fn cachix_action(cachix_filter: &str) -> Step<Use> {
|
||||
named::uses(
|
||||
"cachix",
|
||||
"cachix-action",
|
||||
"0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad", // v16
|
||||
)
|
||||
.add_with(("name", "zed"))
|
||||
.add_with(("authToken", vars::CACHIX_AUTH_TOKEN))
|
||||
.add_with(("pushFilter", cachix_filter))
|
||||
.add_with(("cachixArgs", "-v"))
|
||||
}
|
||||
|
||||
pub fn build(flake_output: &str) -> Step<Run> {
|
||||
named::run(&format!(
|
||||
"nix build .#{} -L --accept-flake-config",
|
||||
flake_output
|
||||
))
|
||||
}
|
||||
|
||||
pub fn limit_store() -> Step<Run> {
|
||||
named::run(indoc! {r#"
|
||||
if [ "$(du -sm /nix/store | cut -f1)" -gt 50000 ]; then
|
||||
nix-collect-garbage -d || true
|
||||
fi"#
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// (janky) helpers to generate steps with a name that corresponds
|
||||
// to the name of the calling function.
|
||||
mod named {
|
||||
use gh_workflow::*;
|
||||
|
||||
pub(super) fn uses(owner: &str, repo: &str, ref_: &str) -> Step<Use> {
|
||||
Step::new(function_name(1)).uses(owner, repo, ref_)
|
||||
}
|
||||
|
||||
pub(super) fn run(script: &str) -> Step<Run> {
|
||||
Step::new(function_name(1))
|
||||
.run(script)
|
||||
.shell("bash -euxo pipefail {0}")
|
||||
}
|
||||
|
||||
fn function_name(i: usize) -> String {
|
||||
let mut name = "<unknown>".to_string();
|
||||
let mut count = 0;
|
||||
backtrace::trace(|frame| {
|
||||
if count < i + 3 {
|
||||
count += 1;
|
||||
return true;
|
||||
}
|
||||
backtrace::resolve_frame(frame, |cb| {
|
||||
if let Some(s) = cb.name() {
|
||||
name = s.to_string()
|
||||
}
|
||||
});
|
||||
false
|
||||
});
|
||||
name.split("::")
|
||||
.skip_while(|s| s != &"steps")
|
||||
.collect::<Vec<_>>()
|
||||
.join("::")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
use gh_workflow::WorkflowCallInput;
|
||||
|
||||
macro_rules! secret {
|
||||
($secret_name:ident) => {
|
||||
pub const $secret_name: &str = concat!("${{ secrets.", stringify!($secret_name), " }}");
|
||||
};
|
||||
}
|
||||
|
||||
secret!(GITHUB_TOKEN);
|
||||
secret!(CACHIX_AUTH_TOKEN);
|
||||
secret!(ZED_CLIENT_CHECKSUM_SEED);
|
||||
secret!(ZED_MINIDUMP_ENDPOINT);
|
||||
secret!(ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON);
|
||||
|
||||
pub fn input(name: &str, input: WorkflowCallInput) -> (String, (&str, WorkflowCallInput)) {
|
||||
return (format!("${{{{ inputs.{name} }}}}"), (name, input));
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
use gh_workflow::*;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::{runners, steps, vars};
|
||||
|
||||
/// Generates the danger.yml workflow
|
||||
pub fn danger() -> Workflow {
|
||||
Workflow::default()
|
||||
.name("Danger")
|
||||
.on(
|
||||
Event::default().pull_request(PullRequest::default().add_branch("main").types([
|
||||
PullRequestType::Opened,
|
||||
PullRequestType::Synchronize,
|
||||
PullRequestType::Reopened,
|
||||
PullRequestType::Edited,
|
||||
])),
|
||||
)
|
||||
.add_job(
|
||||
"danger",
|
||||
Job::default()
|
||||
.cond(Expression::new(
|
||||
"github.repository_owner == 'zed-industries'",
|
||||
))
|
||||
.runs_on(runners::LINUX_CHEAP)
|
||||
.add_step(steps::checkout_repo())
|
||||
.add_step(steps::setup_pnpm())
|
||||
.add_step(steps::danger::setup_node())
|
||||
.add_step(steps::danger::install_deps())
|
||||
.add_step(steps::danger::run()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates the nix.yml workflow
|
||||
pub fn nix() -> Workflow {
|
||||
let env: IndexMap<_, _> = [
|
||||
("ZED_CLIENT_CHECKSUM_SEED", vars::ZED_CLIENT_CHECKSUM_SEED),
|
||||
("ZED_MINIDUMP_ENDPOINT", vars::ZED_MINIDUMP_ENDPOINT),
|
||||
(
|
||||
"ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON",
|
||||
vars::ZED_CLOUD_PROVIDER_ADDITIONAL_MODELS_JSON,
|
||||
),
|
||||
("GIT_LFS_SKIP_SMUDGE", "1"), // breaks the livekit rust sdk examples which we don't actually depend on
|
||||
]
|
||||
.into_iter()
|
||||
.map(|(key, value)| (key.into(), value.into()))
|
||||
.collect();
|
||||
|
||||
// todo(ci) instead of having these as optional YAML inputs,
|
||||
// should we just generate two copies of the job (one for release-nightly
|
||||
// and one for CI?)
|
||||
let (input_flake_output, flake_output) = vars::input(
|
||||
"flake-output",
|
||||
WorkflowCallInput {
|
||||
input_type: "string".into(),
|
||||
default: Some("default".into()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let (input_cachix_filter, cachix_filter) = vars::input(
|
||||
"cachix-filter",
|
||||
WorkflowCallInput {
|
||||
input_type: "string".into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
Workflow::default()
|
||||
.name("Nix build")
|
||||
.on(Event::default().workflow_call(
|
||||
WorkflowCall::default()
|
||||
.add_input(flake_output.0, flake_output.1)
|
||||
.add_input(cachix_filter.0, cachix_filter.1),
|
||||
))
|
||||
.add_job(
|
||||
"nix-build-linux-x86",
|
||||
Job::default()
|
||||
.timeout_minutes(60u32)
|
||||
.continue_on_error(true)
|
||||
.cond(Expression::new(
|
||||
"github.repository_owner == 'zed-industries'",
|
||||
))
|
||||
.runs_on(runners::LINUX_DEFAULT)
|
||||
.env(env.clone())
|
||||
.add_step(steps::checkout_repo().add_with(("clean", "false")))
|
||||
.add_step(steps::nix::install_nix())
|
||||
.add_step(steps::nix::cachix_action(&input_cachix_filter))
|
||||
.add_step(steps::nix::build(&input_flake_output)),
|
||||
)
|
||||
.add_job(
|
||||
"nix-build-mac-arm",
|
||||
Job::default()
|
||||
.timeout_minutes(60u32)
|
||||
.continue_on_error(true)
|
||||
.cond(Expression::new(
|
||||
"github.repository_owner == 'zed-industries'",
|
||||
))
|
||||
.runs_on(runners::MAC_DEFAULT)
|
||||
.env(env)
|
||||
.add_step(steps::checkout_repo().add_with(("clean", "false")))
|
||||
.add_step(steps::nix::set_path())
|
||||
.add_step(steps::nix::cachix_action(&input_cachix_filter))
|
||||
.add_step(steps::nix::build(&input_flake_output))
|
||||
.add_step(steps::nix::limit_store()),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user