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