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