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
 44    fn extension(f: fn() -> Workflow) -> WorkflowFile {
 45        WorkflowFile {
 46            source: f,
 47            r#type: WorkflowType::ExtensionCI,
 48        }
 49    }
 50
 51    fn extension_shared(f: fn() -> Workflow) -> WorkflowFile {
 52        WorkflowFile {
 53            source: f,
 54            r#type: WorkflowType::ExtensionsShared,
 55        }
 56    }
 57
 58    fn generate_file(&self) -> Result<()> {
 59        let workflow = (self.source)();
 60        let workflow_folder = self.r#type.folder_path();
 61
 62        fs::create_dir_all(&workflow_folder).with_context(|| {
 63            format!("Failed to create directory: {}", workflow_folder.display())
 64        })?;
 65
 66        let workflow_name = workflow
 67            .name
 68            .as_ref()
 69            .expect("Workflow must have a name at this point");
 70        let filename = format!(
 71            "{}.yml",
 72            workflow_name.rsplit("::").next().unwrap_or(workflow_name)
 73        );
 74
 75        let workflow_path = workflow_folder.join(filename);
 76
 77        let content = workflow
 78            .to_string()
 79            .map_err(|e| anyhow::anyhow!("{:?}: {:?}", workflow_path, e))?;
 80
 81        let disclaimer = self.r#type.disclaimer(workflow_name);
 82
 83        let content = [disclaimer, content].join("\n");
 84        fs::write(&workflow_path, content).map_err(Into::into)
 85    }
 86}
 87
 88#[derive(PartialEq, Eq)]
 89enum WorkflowType {
 90    /// Workflows living in the Zed repository
 91    Zed,
 92    /// Workflows living in the `zed-extensions/workflows` repository that are
 93    /// required workflows for PRs to the extension organization
 94    ExtensionCI,
 95    /// Workflows living in each of the extensions to perform checks and version
 96    /// bumps until a better, more centralized system for that is in place.
 97    ExtensionsShared,
 98}
 99
100impl WorkflowType {
101    fn disclaimer(&self, workflow_name: &str) -> String {
102        format!(
103            concat!(
104                "# Generated from xtask::workflows::{}{}\n",
105                "# Rebuild with `cargo xtask workflows`.",
106            ),
107            workflow_name,
108            (*self != WorkflowType::Zed)
109                .then_some(" within the Zed repository.")
110                .unwrap_or_default(),
111        )
112    }
113
114    fn folder_path(&self) -> PathBuf {
115        match self {
116            WorkflowType::Zed => PathBuf::from(".github/workflows"),
117            WorkflowType::ExtensionCI => PathBuf::from("extensions/workflows"),
118            WorkflowType::ExtensionsShared => PathBuf::from("extensions/workflows/shared"),
119        }
120    }
121}
122
123pub fn run_workflows(_: GenerateWorkflowArgs) -> Result<()> {
124    if !Path::new("crates/zed/").is_dir() {
125        anyhow::bail!("xtask workflows must be ran from the project root");
126    }
127
128    let workflows = [
129        WorkflowFile::zed(danger::danger),
130        WorkflowFile::zed(run_bundling::run_bundling),
131        WorkflowFile::zed(release_nightly::release_nightly),
132        WorkflowFile::zed(run_tests::run_tests),
133        WorkflowFile::zed(release::release),
134        WorkflowFile::zed(cherry_pick::cherry_pick),
135        WorkflowFile::zed(autofix_pr::autofix_pr),
136        WorkflowFile::zed(compare_perf::compare_perf),
137        WorkflowFile::zed(run_agent_evals::run_unit_evals),
138        WorkflowFile::zed(run_agent_evals::run_cron_unit_evals),
139        WorkflowFile::zed(run_agent_evals::run_agent_evals),
140        WorkflowFile::zed(after_release::after_release),
141        WorkflowFile::zed(extension_tests::extension_tests),
142        WorkflowFile::zed(extension_bump::extension_bump),
143        WorkflowFile::zed(extension_release::extension_release),
144        WorkflowFile::zed(extension_workflow_rollout::extension_workflow_rollout),
145        /* workflows used for CI/CD in extension repositories */
146        WorkflowFile::extension(extensions::run_tests::run_tests),
147        WorkflowFile::extension_shared(extensions::bump_version::bump_version),
148        WorkflowFile::extension_shared(extensions::release_version::release_version),
149    ];
150
151    for workflow_file in workflows {
152        workflow_file.generate_file()?;
153    }
154
155    Ok(())
156}