run_tests.rs

  1use gh_workflow::{Concurrency, Event, Expression, PullRequest, Push, Run, Step, Use, Workflow};
  2
  3use super::{
  4    runners::{self, Platform},
  5    steps::{self, FluentBuilder, NamedJob, named, release_job},
  6};
  7
  8fn str_vec(values: &'static [&'static str]) -> Vec<String> {
  9    values.into_iter().map(ToString::to_string).collect()
 10}
 11
 12pub(crate) fn run_tests_in(paths: &'static [&'static str], workflow: Workflow) -> Workflow {
 13    let paths = str_vec(paths);
 14    workflow
 15        .add_event(Event::default()
 16            .push(
 17                Push::default()
 18                    .branches(
 19                        [
 20                            "main",
 21                            "v[0-9]+.[0-9]+.x", // any release branch
 22                        ]
 23                        .map(String::from),
 24                    )
 25                    .paths(paths.clone())
 26                ,
 27            )
 28            .pull_request(
 29                PullRequest::default().branches(
 30                    [
 31                        "**", // all branches
 32                    ]
 33                    .map(String::from),
 34                )
 35                .paths(paths),
 36            ))
 37        .concurrency(Concurrency::default()
 38            .group("${{ github.workflow }}-${{ github.ref_name }}-${{ github.ref_name == 'main' && github.sha || 'anysha' }}")
 39            .cancel_in_progress(true)
 40        )
 41        .add_env(( "CARGO_TERM_COLOR", "always" ))
 42        .add_env(( "RUST_BACKTRACE", 1 ))
 43        .add_env(( "CARGO_INCREMENTAL", 0 ))
 44}
 45
 46pub(crate) fn run_tests() -> Workflow {
 47    let windows_tests = run_platform_tests(Platform::Windows);
 48    let linux_tests = run_platform_tests(Platform::Linux);
 49    let mac_tests = run_platform_tests(Platform::Mac);
 50    let migrations = check_postgres_and_protobuf_migrations();
 51    let doctests = doctests();
 52    let check_dependencies = check_dependencies();
 53    let check_other_binaries = check_workspace_binaries();
 54
 55    named::workflow()
 56        .map(|workflow| {
 57            run_tests_in(
 58                &[
 59                    "!docs/**",
 60                    "!script/update_top_ranking_issues/**",
 61                    "!.github/ISSUE_TEMPLATE/**",
 62                    "!.github/workflows/**",
 63                    ".github/workflows/run_tests.yml", // re-include this workflow so it re-runs when changed
 64                ],
 65                workflow,
 66            )
 67        })
 68        .add_job(windows_tests.name, windows_tests.job)
 69        .add_job(linux_tests.name, linux_tests.job)
 70        .add_job(mac_tests.name, mac_tests.job)
 71        .add_job(migrations.name, migrations.job)
 72        .add_job(doctests.name, doctests.job)
 73        .add_job(check_dependencies.name, check_dependencies.job)
 74        .add_job(check_other_binaries.name, check_other_binaries.job)
 75}
 76
 77fn check_dependencies() -> NamedJob {
 78    fn install_cargo_machete() -> Step<Use> {
 79        named::uses(
 80            "clechasseur",
 81            "rs-cargo",
 82            "8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386", // v2
 83        )
 84        .add_with(("command", "install"))
 85        .add_with(("args", "cargo-machete@0.7.0"))
 86    }
 87
 88    fn run_cargo_machete() -> Step<Use> {
 89        named::uses(
 90            "clechasseur",
 91            "rs-cargo",
 92            "8435b10f6e71c2e3d4d3b7573003a8ce4bfc6386", // v2
 93        )
 94        .add_with(("command", "machete"))
 95    }
 96
 97    fn check_cargo_lock() -> Step<Run> {
 98        named::bash("cargo update --locked --workspace")
 99    }
100
101    fn check_vulnerable_dependencies() -> Step<Use> {
102        named::uses(
103            "actions",
104            "dependency-review-action",
105            "67d4f4bd7a9b17a0db54d2a7519187c65e339de8", // v4
106        )
107        .if_condition(Expression::new("github.event_name == 'pull_request'"))
108        .with(("license-check", false))
109    }
110
111    named::job(
112        release_job(&[])
113            .runs_on(runners::LINUX_SMALL)
114            .add_step(steps::checkout_repo())
115            .add_step(install_cargo_machete())
116            .add_step(run_cargo_machete())
117            .add_step(check_cargo_lock())
118            .add_step(check_vulnerable_dependencies()),
119    )
120}
121
122fn check_workspace_binaries() -> NamedJob {
123    named::job(
124        release_job(&[])
125            .runs_on(runners::LINUX_LARGE)
126            .add_step(steps::checkout_repo())
127            .add_step(steps::setup_cargo_config(Platform::Linux))
128            .map(steps::install_linux_dependencies)
129            .add_step(steps::script("cargo build -p collab"))
130            .add_step(steps::script("cargo build --workspace --bins --examples"))
131            .add_step(steps::cleanup_cargo_config(Platform::Linux)),
132    )
133}
134
135pub(crate) fn run_platform_tests(platform: Platform) -> NamedJob {
136    let runner = match platform {
137        Platform::Windows => runners::WINDOWS_DEFAULT,
138        Platform::Linux => runners::LINUX_DEFAULT,
139        Platform::Mac => runners::MAC_DEFAULT,
140    };
141    NamedJob {
142        name: format!("run_tests_{platform}"),
143        job: release_job(&[])
144            .runs_on(runner)
145            .add_step(steps::checkout_repo())
146            .add_step(steps::setup_cargo_config(platform))
147            .when(
148                platform == Platform::Linux,
149                steps::install_linux_dependencies,
150            )
151            .add_step(steps::setup_node())
152            .add_step(steps::clippy(platform))
153            .add_step(steps::cargo_install_nextest(platform))
154            .add_step(steps::clear_target_dir_if_large(platform))
155            .add_step(steps::cargo_nextest(platform))
156            .add_step(steps::cleanup_cargo_config(platform)),
157    }
158}
159
160pub(crate) fn check_postgres_and_protobuf_migrations() -> NamedJob {
161    fn remove_untracked_files() -> Step<Run> {
162        named::bash("git clean -df")
163    }
164
165    fn ensure_fresh_merge() -> Step<Run> {
166        named::bash(indoc::indoc! {r#"
167            if [ -z "$GITHUB_BASE_REF" ];
168            then
169              echo "BUF_BASE_BRANCH=$(git merge-base origin/main HEAD)" >> "$GITHUB_ENV"
170            else
171              git checkout -B temp
172              git merge -q "origin/$GITHUB_BASE_REF" -m "merge main into temp"
173              echo "BUF_BASE_BRANCH=$GITHUB_BASE_REF" >> "$GITHUB_ENV"
174            fi
175        "#})
176    }
177
178    fn bufbuild_setup_action() -> Step<Use> {
179        named::uses("bufbuild", "buf-setup-action", "v1").add_with(("version", "v1.29.0"))
180    }
181
182    fn bufbuild_breaking_action() -> Step<Use> {
183        named::uses("bufbuild", "buf-breaking-action", "v1").add_with(("input", "crates/proto/proto/"))
184            .add_with(("against", "https://github.com/${GITHUB_REPOSITORY}.git#branch=${BUF_BASE_BRANCH},subdir=crates/proto/proto/"))
185    }
186
187    named::job(
188        release_job(&[])
189            .runs_on(runners::MAC_DEFAULT)
190            .add_step(steps::checkout_repo().with(("fetch-depth", 0))) // fetch full history
191            .add_step(remove_untracked_files())
192            .add_step(ensure_fresh_merge())
193            .add_step(bufbuild_setup_action())
194            .add_step(bufbuild_breaking_action()),
195    )
196}
197
198fn doctests() -> NamedJob {
199    fn run_doctests() -> Step<Run> {
200        named::bash(indoc::indoc! {r#"
201            cargo test --workspace --doc --no-fail-fast
202        "#})
203        .id("run_doctests")
204    }
205
206    named::job(
207        release_job(&[])
208            .runs_on(runners::LINUX_DEFAULT)
209            .add_step(steps::checkout_repo())
210            .add_step(steps::cache_rust_dependencies())
211            .map(steps::install_linux_dependencies)
212            .add_step(steps::setup_cargo_config(Platform::Linux))
213            .add_step(run_doctests())
214            .add_step(steps::cleanup_cargo_config(Platform::Linux)),
215    )
216}