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