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