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