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