workflows.rs

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