1use gh_workflow::*;
2use indoc::{formatdoc, indoc};
3
4use crate::tasks::workflows::{
5 extension_bump::compare_versions,
6 run_tests::{orchestrate_without_package_filter, tests_pass},
7 runners,
8 steps::{
9 self, CommonJobConditions, FluentBuilder, NamedJob, cache_rust_dependencies_namespace,
10 named,
11 },
12 vars::{PathCondition, StepOutput, one_workflow_per_non_main_branch},
13};
14
15pub(crate) const ZED_EXTENSION_CLI_SHA: &str = "03d8e9aee95ea6117d75a48bcac2e19241f6e667";
16
17// This should follow the set target in crates/extension/src/extension_builder.rs
18const EXTENSION_RUST_TARGET: &str = "wasm32-wasip2";
19
20// This is used by various extensions repos in the zed-extensions org to run automated tests.
21pub(crate) fn extension_tests() -> Workflow {
22 let should_check_rust = PathCondition::new("check_rust", r"^(Cargo.lock|Cargo.toml|.*\.rs)$");
23 let should_check_extension =
24 PathCondition::new("check_extension", r"^(extension\.toml|.*\.scm)$");
25
26 let orchestrate =
27 orchestrate_without_package_filter(&[&should_check_rust, &should_check_extension]);
28
29 let jobs = [
30 orchestrate,
31 should_check_rust.guard(check_rust()),
32 should_check_extension.guard(check_extension()),
33 ];
34
35 let tests_pass = tests_pass(&jobs);
36
37 named::workflow()
38 .add_event(Event::default().workflow_call(WorkflowCall::default()))
39 .concurrency(one_workflow_per_non_main_branch())
40 .add_env(("CARGO_TERM_COLOR", "always"))
41 .add_env(("RUST_BACKTRACE", 1))
42 .add_env(("CARGO_INCREMENTAL", 0))
43 .add_env(("ZED_EXTENSION_CLI_SHA", ZED_EXTENSION_CLI_SHA))
44 .add_env(("RUSTUP_TOOLCHAIN", "stable"))
45 .add_env(("CARGO_BUILD_TARGET", EXTENSION_RUST_TARGET))
46 .map(|workflow| {
47 jobs.into_iter()
48 .chain([tests_pass])
49 .fold(workflow, |workflow, job| {
50 workflow.add_job(job.name, job.job)
51 })
52 })
53}
54
55fn install_rust_target() -> Step<Run> {
56 named::bash(format!("rustup target add {EXTENSION_RUST_TARGET}",))
57}
58
59fn run_clippy() -> Step<Run> {
60 named::bash("cargo clippy --release --all-features -- --deny warnings")
61}
62
63fn check_rust() -> NamedJob {
64 let job = Job::default()
65 .with_repository_owner_guard()
66 .runs_on(runners::LINUX_LARGE_RAM)
67 .timeout_minutes(6u32)
68 .add_step(steps::checkout_repo())
69 .add_step(steps::cache_rust_dependencies_namespace())
70 .add_step(install_rust_target())
71 .add_step(steps::cargo_fmt())
72 .add_step(run_clippy())
73 .add_step(steps::cargo_install_nextest())
74 .add_step(
75 steps::cargo_nextest(runners::Platform::Linux)
76 // Set the target to the current platform again
77 .with_target("$(rustc -vV | sed -n 's|host: ||p')")
78 .add_env(("NEXTEST_NO_TESTS", "warn")),
79 );
80
81 named::job(job)
82}
83
84pub(crate) fn check_extension() -> NamedJob {
85 let (cache_download, cache_hit) = cache_zed_extension_cli();
86 let (check_version_job, version_changed, _) = compare_versions();
87
88 let job = Job::default()
89 .with_repository_owner_guard()
90 .runs_on(runners::LINUX_LARGE_RAM)
91 .timeout_minutes(6u32)
92 .add_step(steps::checkout_repo().with_full_history())
93 .add_step(cache_download)
94 .add_step(download_zed_extension_cli(cache_hit))
95 .add_step(cache_rust_dependencies_namespace()) // Extensions can compile Rust, so provide the cache if needed.
96 .add_step(check())
97 .add_step(check_version_job)
98 .add_step(verify_version_did_not_change(version_changed))
99 .add_step(breakpoint());
100
101 named::job(job)
102}
103
104fn breakpoint() -> Step<Use> {
105 named::uses(
106 "namespacelabs",
107 "breakpoint-action",
108 "ca62bf12510ebf1115a560cf337a35fad5eb052b",
109 )
110 .if_condition(Expression::new("failure()"))
111 .add_with(("duration", "15m"))
112 .add_with(("authorized-users", "MrSubidubi"))
113}
114
115pub fn cache_zed_extension_cli() -> (Step<Use>, StepOutput) {
116 let step = named::uses(
117 "actions",
118 "cache",
119 "0057852bfaa89a56745cba8c7296529d2fc39830",
120 )
121 .id("cache-zed-extension-cli")
122 .with(
123 Input::default()
124 .add("path", "zed-extension")
125 .add("key", "zed-extension-${{ env.ZED_EXTENSION_CLI_SHA }}"),
126 );
127 let output = StepOutput::new(&step, "cache-hit");
128 (step, output)
129}
130
131pub fn download_zed_extension_cli(cache_hit: StepOutput) -> Step<Run> {
132 named::bash(
133 indoc! {
134 r#"
135 wget --quiet "https://zed-extension-cli.nyc3.digitaloceanspaces.com/$ZED_EXTENSION_CLI_SHA/x86_64-unknown-linux-gnu/zed-extension"
136 chmod +x zed-extension
137 "#,
138 }
139 ).if_condition(Expression::new(format!("{} != 'true'", cache_hit.expr())))
140}
141
142pub fn check() -> Step<Run> {
143 named::bash(indoc! {
144 r#"
145 mkdir -p /tmp/ext-scratch
146 mkdir -p /tmp/ext-output
147 ./zed-extension --source-dir . --scratch-dir /tmp/ext-scratch --output-dir /tmp/ext-output
148 "#
149 })
150}
151
152fn verify_version_did_not_change(version_changed: StepOutput) -> Step<Run> {
153 named::bash(formatdoc! {r#"
154 if [[ {version_changed} == "true" && "${{{{ github.event_name }}}}" == "pull_request" && "${{{{ github.event.pull_request.user.login }}}}" != "zed-zippy[bot]" ]] ; then
155 echo "Version change detected in your change!"
156 echo "Version changes happen in separate PRs and will be performed by the zed-zippy bot"
157 exit 42
158 fi
159 "#
160 })
161}