workflows.rs

  1use anyhow::{Context, Result};
  2use clap::Parser;
  3use gh_workflow::Workflow;
  4use std::fs;
  5use std::path::{Path, PathBuf};
  6
  7mod after_release;
  8mod autofix_pr;
  9mod cherry_pick;
 10mod compare_perf;
 11mod danger;
 12mod extension_bump;
 13mod extension_release;
 14mod extension_tests;
 15mod extensions;
 16mod nix_build;
 17mod release_nightly;
 18mod run_bundling;
 19
 20mod release;
 21mod run_agent_evals;
 22mod run_tests;
 23mod runners;
 24mod steps;
 25mod vars;
 26
 27#[derive(Parser)]
 28pub struct GenerateWorkflowArgs {}
 29
 30struct WorkflowFile {
 31    source: fn() -> Workflow,
 32    r#type: WorkflowType,
 33}
 34
 35impl WorkflowFile {
 36    fn zed(f: fn() -> Workflow) -> WorkflowFile {
 37        WorkflowFile {
 38            source: f,
 39            r#type: WorkflowType::Zed,
 40        }
 41    }
 42    fn extension(f: fn() -> Workflow) -> WorkflowFile {
 43        WorkflowFile {
 44            source: f,
 45            r#type: WorkflowType::Extensions,
 46        }
 47    }
 48
 49    fn generate_file(&self) -> Result<()> {
 50        let workflow = (self.source)();
 51        let workflow_folder = self.r#type.folder_path();
 52        let workflow_name = workflow
 53            .name
 54            .as_ref()
 55            .expect("Workflow must have a name at this point");
 56        let filename = format!(
 57            "{}.yml",
 58            workflow_name.rsplit("::").next().unwrap_or(workflow_name)
 59        );
 60
 61        let workflow_path = workflow_folder.join(filename);
 62
 63        let content = workflow
 64            .to_string()
 65            .map_err(|e| anyhow::anyhow!("{:?}: {:?}", workflow_path, e))?;
 66
 67        let disclaimer = self.r#type.disclaimer(workflow_name);
 68
 69        let content = [disclaimer, content].join("\n");
 70        fs::write(&workflow_path, content).map_err(Into::into)
 71    }
 72}
 73
 74enum WorkflowType {
 75    Zed,
 76    Extensions,
 77}
 78
 79impl WorkflowType {
 80    fn disclaimer(&self, workflow_name: &str) -> String {
 81        format!(
 82            concat!(
 83                "# Generated from xtask::workflows::{}{}\n",
 84                "# Rebuild with `cargo xtask workflows`.",
 85            ),
 86            workflow_name,
 87            matches!(self, WorkflowType::Extensions)
 88                .then_some(" within the Zed repository.")
 89                .unwrap_or_default(),
 90        )
 91    }
 92
 93    fn folder_path(&self) -> PathBuf {
 94        match self {
 95            WorkflowType::Zed => PathBuf::from(".github/workflows"),
 96            WorkflowType::Extensions => PathBuf::from("extensions/workflows"),
 97        }
 98    }
 99}
100
101pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
102    if !Path::new("crates/zed/").is_dir() {
103        anyhow::bail!("xtask workflows must be ran from the project root");
104    }
105    let workflow_dir = Path::new(".github/workflows");
106    let extension_workflow_dir = Path::new("extensions/workflows");
107
108    let workflows = [
109        WorkflowFile::zed(danger::danger),
110        WorkflowFile::zed(run_bundling::run_bundling),
111        WorkflowFile::zed(release_nightly::release_nightly),
112        WorkflowFile::zed(run_tests::run_tests),
113        WorkflowFile::zed(release::release),
114        WorkflowFile::zed(cherry_pick::cherry_pick),
115        WorkflowFile::zed(autofix_pr::autofix_pr),
116        WorkflowFile::zed(compare_perf::compare_perf),
117        WorkflowFile::zed(run_agent_evals::run_unit_evals),
118        WorkflowFile::zed(run_agent_evals::run_cron_unit_evals),
119        WorkflowFile::zed(run_agent_evals::run_agent_evals),
120        WorkflowFile::zed(after_release::after_release),
121        WorkflowFile::zed(extension_tests::extension_tests),
122        WorkflowFile::zed(extension_bump::extension_bump),
123        WorkflowFile::zed(extension_release::extension_release),
124        /* workflows used for CI/CD in extension repositories */
125        WorkflowFile::extension(extensions::run_tests::run_tests),
126        WorkflowFile::extension(extensions::bump_version::bump_version),
127        WorkflowFile::extension(extensions::release_version::release_version),
128    ];
129
130    for directory in [&workflow_dir, &extension_workflow_dir] {
131        fs::create_dir_all(directory)
132            .with_context(|| format!("Failed to create directory: {}", directory.display()))?;
133    }
134
135    for workflow_file in workflows {
136        workflow_file.generate_file()?;
137    }
138
139    Ok(())
140}