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