extension_tests.rs

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