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