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