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