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