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