atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
xtask/src/ci.rsRust521 lines16.0 KB
1
use std::{
2
    collections::BTreeMap,
3
    env::consts::{DLL_PREFIX, DLL_SUFFIX},
4
    fmt::Display,
5
};
6
7
use clap::{Args, Subcommand, ValueEnum};
8
use xshell::cmd;
9
10
use crate::{DenyWarnings, NIGHTLY, Result, build_docs, sh, workspace};
11
12
const WASM_TIMEOUT_ENV_KEY: &str = "WASM_BINDGEN_TEST_TIMEOUT";
13
const WASM_TIMEOUT_VALUE: &str = "180";
14
15
#[derive(Args)]
16
pub struct CiArgs {
17
    #[clap(subcommand)]
18
    cmd: Option<CiCommand>,
19
}
20
21
/// The kind of runner for WebAssembly tests run with `wasm-pack test`.
22
#[derive(Clone, Copy, Debug, Default, ValueEnum)]
23
enum WasmTestRunner {
24
    // Run with all available runners.
25
    #[default]
26
    All,
27
    // Run with the Node.js runner.
28
    Node,
29
    // Run with the Firefox runner.
30
    Firefox,
31
    // Run with the Chrome runner.
32
    Chrome,
33
}
34
35
impl Display for WasmTestRunner {
36
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37
        match self {
38
            WasmTestRunner::All => write!(f, "all"),
39
            WasmTestRunner::Node => write!(f, "node"),
40
            WasmTestRunner::Firefox => write!(f, "firefox"),
41
            WasmTestRunner::Chrome => write!(f, "chrome"),
42
        }
43
    }
44
}
45
46
#[derive(Subcommand)]
47
enum CiCommand {
48
    /// Check style
49
    Style,
50
51
    /// Check for typos
52
    Typos,
53
54
    /// Check clippy lints
55
    Clippy,
56
57
    /// Check documentation
58
    Docs,
59
60
    /// Run tests with a specific feature set
61
    TestFeatures {
62
        #[clap(subcommand)]
63
        cmd: Option<FeatureSet>,
64
    },
65
66
    /// Run clippy checks for the wasm target
67
    Wasm {
68
        #[clap(subcommand)]
69
        cmd: Option<WasmFeatureSet>,
70
    },
71
72
    /// Run tests with `wasm-pack test`
73
    WasmPack {
74
        #[clap(subcommand)]
75
        cmd: Option<WasmFeatureSet>,
76
77
        #[clap(long, default_value_t = WasmTestRunner::All)]
78
        runner: WasmTestRunner,
79
    },
80
81
    /// Run tests for the different crypto crate features
82
    TestCrypto,
83
84
    /// Check that bindings can be generated
85
    Bindings,
86
87
    /// Check that the examples compile
88
    Examples,
89
90
    /// Run the workspace tests and create a code coverage report using
91
    /// llvm-cov.
92
    ///
93
    /// Note: This requires the docker container for the integration tests to be
94
    /// running.
95
    Coverage {
96
        /// Specify the output format that we're going to use.
97
        #[arg(long, short, default_value_t = CoverageOutputFormat::Text)]
98
        output_format: CoverageOutputFormat,
99
    },
100
}
101
102
#[derive(Clone, Debug, Default, ValueEnum)]
103
enum CoverageOutputFormat {
104
    /// Output the coverage report to stdout.
105
    #[default]
106
    Text,
107
    /// Output the coverage report as a HTML report in the target/llvm-cov/html
108
    /// folder.
109
    Html,
110
    /// Output the coverage report as the custom Codecov coverage format.
111
    /// folder.
112
    Codecov,
113
}
114
115
impl Display for CoverageOutputFormat {
116
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117
        match self {
118
            CoverageOutputFormat::Text => write!(f, "text"),
119
            CoverageOutputFormat::Html => write!(f, "html"),
120
            CoverageOutputFormat::Codecov => write!(f, "codecov"),
121
        }
122
    }
123
}
124
125
#[derive(Subcommand, PartialEq, Eq, PartialOrd, Ord)]
126
enum FeatureSet {
127
    NoEncryption,
128
    NoSqlite,
129
    NoEncryptionAndSqlite,
130
    SqliteCryptostore,
131
    ExperimentalEncryptedStateEvents,
132
    Markdown,
133
    Socks,
134
    SsoLogin,
135
    Search,
136
    ElementRecentEmojis,
137
}
138
139
#[derive(Subcommand, PartialEq, Eq, PartialOrd, Ord)]
140
#[allow(clippy::enum_variant_names)]
141
enum WasmFeatureSet {
142
    /// Check `matrix-sdk-qrcode` crate
143
    MatrixSdkQrcode,
144
    /// Check `matrix-sdk-base` crate
145
    MatrixSdkBase,
146
    /// Check `matrix-sdk-common` crate
147
    MatrixSdkCommon,
148
    /// Check `matrix-sdk` crate with no default features
149
    MatrixSdkNoDefault,
150
    /// Check `matrix-sdk-ui` crate
151
    MatrixSdkUi,
152
    /// Check `matrix-sdk` crate with `indexeddb` feature (but not
153
    /// `e2e-encryption`)
154
    MatrixSdkIndexeddbStoresNoCrypto,
155
    /// Check `matrix-sdk` crate with `indexeddb` and `e2e-encryption` features
156
    MatrixSdkIndexeddbStores,
157
    /// Check `matrix-sdk-indexeddb` crate with all features
158
    IndexeddbAllFeatures,
159
    /// Check `matrix-sdk-indexeddb` crate with `e2e-encryption` feature
160
    IndexeddbCrypto,
161
    /// Check `matrix-sdk-indexeddb` crate with `state-store` feature
162
    IndexeddbState,
163
    /// Equivalent to `indexeddb-all-features`, `indexeddb-crypto` and
164
    /// `indexeddb-state`
165
    Indexeddb,
166
}
167
168
impl CiArgs {
169
    pub fn run(self) -> Result<()> {
170
        let sh = sh();
171
        let _p = sh.push_dir(workspace::root_path()?);
172
173
        match self.cmd {
174
            Some(cmd) => match cmd {
175
                CiCommand::Style => check_style(),
176
                CiCommand::Typos => check_typos(),
177
                CiCommand::Clippy => check_clippy(),
178
                CiCommand::Docs => check_docs(),
179
                CiCommand::TestFeatures { cmd } => run_feature_tests(cmd),
180
                CiCommand::Wasm { cmd } => run_wasm_checks(cmd),
181
                CiCommand::WasmPack { cmd, runner } => run_wasm_pack_tests(cmd, runner),
182
                CiCommand::TestCrypto => run_crypto_tests(),
183
                CiCommand::Bindings => check_bindings(),
184
                CiCommand::Examples => check_examples(),
185
                CiCommand::Coverage { output_format } => run_coverage(output_format),
186
            },
187
            None => {
188
                check_style()?;
189
                check_clippy()?;
190
                check_typos()?;
191
                check_docs()?;
192
                run_feature_tests(None)?;
193
                run_wasm_checks(None)?;
194
                run_crypto_tests()?;
195
                check_examples()?;
196
197
                Ok(())
198
            }
199
        }
200
    }
201
}
202
203
fn check_bindings() -> Result<()> {
204
    let sh = sh();
205
    cmd!(sh, "rustup run stable cargo build -p matrix-sdk-crypto-ffi -p matrix-sdk-ffi --features sentry,experimental-element-recent-emojis").run()?;
206
    cmd!(
207
        sh,
208
        "
209
        rustup run stable cargo run -p uniffi-bindgen -- generate
210
            --library
211
            --language kotlin
212
            --language swift
213
            --out-dir target/generated-bindings
214
            target/debug/{DLL_PREFIX}matrix_sdk_ffi{DLL_SUFFIX}
215
        "
216
    )
217
    .run()?;
218
    cmd!(
219
        sh,
220
        "
221
        rustup run stable cargo run -p uniffi-bindgen -- generate
222
            --library
223
            --language kotlin
224
            --language swift
225
            --out-dir target/generated-bindings
226
            target/debug/{DLL_PREFIX}matrix_sdk_crypto_ffi{DLL_SUFFIX}
227
        "
228
    )
229
    .run()?;
230
231
    Ok(())
232
}
233
234
fn check_examples() -> Result<()> {
235
    let sh = sh();
236
    cmd!(sh, "rustup run stable cargo check -p example-*").run()?;
237
    Ok(())
238
}
239
240
fn check_style() -> Result<()> {
241
    let sh = sh();
242
    cmd!(sh, "rustup run {NIGHTLY} cargo fmt -- --check").run()?;
243
    Ok(())
244
}
245
246
fn check_typos() -> Result<()> {
247
    let sh = sh();
248
    // FIXME: Print install instructions if command-not-found (needs an xshell
249
    //        change: https://github.com/matklad/xshell/issues/46)
250
    cmd!(sh, "typos").run()?;
251
    Ok(())
252
}
253
254
fn check_clippy() -> Result<()> {
255
    let sh = sh();
256
    cmd!(sh, "rustup run {NIGHTLY} cargo clippy --all-targets --features testing -- -D warnings")
257
        .run()?;
258
259
    cmd!(
260
        sh,
261
        "rustup run {NIGHTLY} cargo clippy --workspace --all-targets
262
            --exclude matrix-sdk-crypto --exclude xtask
263
            --no-default-features
264
            --features sso-login,sqlite,testing,experimental-element-recent-emojis
265
            -- -D warnings"
266
    )
267
    .run()?;
268
269
    cmd!(
270
        sh,
271
        "rustup run {NIGHTLY} cargo clippy --all-targets -p matrix-sdk-crypto
272
            --no-default-features -- -D warnings"
273
    )
274
    .run()?;
275
276
    Ok(())
277
}
278
279
fn check_docs() -> Result<()> {
280
    build_docs([], DenyWarnings::Yes)
281
}
282
283
fn run_feature_tests(cmd: Option<FeatureSet>) -> Result<()> {
284
    let args = BTreeMap::from([
285
        (FeatureSet::NoEncryption, "--no-default-features --features sqlite,testing"),
286
        (FeatureSet::NoSqlite, "--no-default-features --features e2e-encryption,testing"),
287
        (FeatureSet::NoEncryptionAndSqlite, "--no-default-features --features testing"),
288
        (
289
            FeatureSet::SqliteCryptostore,
290
            "--no-default-features --features e2e-encryption,sqlite,testing",
291
        ),
292
        (
293
            FeatureSet::ExperimentalEncryptedStateEvents,
294
            "--no-default-features --features experimental-encrypted-state-events,e2e-encryption,sqlite,testing",
295
        ),
296
        (FeatureSet::Markdown, "--features markdown,testing"),
297
        (FeatureSet::Socks, "--features socks,testing"),
298
        (FeatureSet::SsoLogin, "--features sso-login,testing"),
299
        (FeatureSet::Search, "--features experimental-search"),
300
        (FeatureSet::ElementRecentEmojis, "--features experimental-element-recent-emojis"),
301
    ]);
302
303
    let sh = sh();
304
    let run = |arg_set: &str| {
305
        cmd!(sh, "rustup run stable cargo nextest run -p matrix-sdk")
306
            .args(arg_set.split_whitespace())
307
            .run()?;
308
        cmd!(sh, "rustup run stable cargo test --doc -p matrix-sdk")
309
            .args(arg_set.split_whitespace())
310
            .run()
311
    };
312
313
    match cmd {
314
        Some(cmd) => {
315
            run(args[&cmd])?;
316
        }
317
        None => {
318
            for &arg_set in args.values() {
319
                run(arg_set)?;
320
            }
321
        }
322
    }
323
324
    Ok(())
325
}
326
327
fn run_crypto_tests() -> Result<()> {
328
    let sh = sh();
329
    cmd!(sh, "rustup run stable cargo clippy -p matrix-sdk-crypto -- -D warnings").run()?;
330
    cmd!(sh, "rustup run stable cargo nextest run -p matrix-sdk-crypto --no-default-features --features testing").run()?;
331
    cmd!(sh, "rustup run stable cargo nextest run -p matrix-sdk-crypto --features=testing")
332
        .run()?;
333
    cmd!(sh, "rustup run stable cargo test --doc -p matrix-sdk-crypto --features=testing").run()?;
334
    cmd!(
335
        sh,
336
        "rustup run stable cargo clippy -p matrix-sdk-crypto --features=experimental-algorithms -- -D warnings"
337
    )
338
    .run()?;
339
    cmd!(
340
        sh,
341
        "rustup run stable cargo nextest run -p matrix-sdk-crypto --features=experimental-algorithms,testing"
342
    ).run()?;
343
    cmd!(
344
        sh,
345
        "rustup run stable cargo test --doc -p matrix-sdk-crypto --features=experimental-algorithms,testing"
346
    )
347
    .run()?;
348
    cmd!(sh, "rustup run stable cargo nextest run -p matrix-sdk-crypto --features=experimental-encrypted-state-events").run()?;
349
350
    cmd!(sh, "rustup run stable cargo nextest run -p matrix-sdk-crypto-ffi").run()?;
351
352
    cmd!(
353
        sh,
354
        "rustup run stable cargo nextest run -p matrix-sdk-sqlite --features crypto-store,testing"
355
    )
356
    .run()?;
357
358
    Ok(())
359
}
360
361
fn run_wasm_checks(cmd: Option<WasmFeatureSet>) -> Result<()> {
362
    if let Some(WasmFeatureSet::Indexeddb) = cmd {
363
        run_wasm_checks(Some(WasmFeatureSet::IndexeddbAllFeatures))?;
364
        run_wasm_checks(Some(WasmFeatureSet::IndexeddbCrypto))?;
365
        run_wasm_checks(Some(WasmFeatureSet::IndexeddbState))?;
366
        return Ok(());
367
    }
368
369
    let args = BTreeMap::from([
370
        (WasmFeatureSet::MatrixSdkQrcode, "-p matrix-sdk-qrcode --features js"),
371
        (WasmFeatureSet::MatrixSdkNoDefault, "-p matrix-sdk --no-default-features --features js"),
372
        (WasmFeatureSet::MatrixSdkBase, "-p matrix-sdk-base --features js,test-send-sync"),
373
        (WasmFeatureSet::MatrixSdkCommon, "-p matrix-sdk-common --features js"),
374
        (WasmFeatureSet::MatrixSdkUi, "-p matrix-sdk-ui --features js"),
375
        (
376
            WasmFeatureSet::MatrixSdkIndexeddbStoresNoCrypto,
377
            "-p matrix-sdk --no-default-features --features js,indexeddb",
378
        ),
379
        (
380
            WasmFeatureSet::MatrixSdkIndexeddbStores,
381
            "-p matrix-sdk --no-default-features --features js,indexeddb,e2e-encryption",
382
        ),
383
        (WasmFeatureSet::IndexeddbAllFeatures, "-p matrix-sdk-indexeddb"),
384
        (
385
            WasmFeatureSet::IndexeddbCrypto,
386
            "-p matrix-sdk-indexeddb --no-default-features --features e2e-encryption",
387
        ),
388
        (
389
            WasmFeatureSet::IndexeddbState,
390
            "-p matrix-sdk-indexeddb --no-default-features --features state-store",
391
        ),
392
    ]);
393
394
    let sh = sh();
395
    let run = |arg_set: &str| {
396
        cmd!(sh, "rustup run stable cargo clippy --target wasm32-unknown-unknown")
397
            .args(arg_set.split_whitespace())
398
            .args(["--", "-D", "warnings"])
399
            .env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
400
            .run()
401
    };
402
403
    match cmd {
404
        Some(cmd) => {
405
            run(args[&cmd])?;
406
        }
407
        None => {
408
            for &arg_set in args.values() {
409
                run(arg_set)?;
410
            }
411
        }
412
    }
413
414
    Ok(())
415
}
416
417
fn run_wasm_pack_tests(cmd: Option<WasmFeatureSet>, runner: WasmTestRunner) -> Result<()> {
418
    if let Some(WasmFeatureSet::Indexeddb) = cmd {
419
        run_wasm_pack_tests(Some(WasmFeatureSet::IndexeddbAllFeatures), runner)?;
420
        run_wasm_pack_tests(Some(WasmFeatureSet::IndexeddbCrypto), runner)?;
421
        run_wasm_pack_tests(Some(WasmFeatureSet::IndexeddbState), runner)?;
422
        return Ok(());
423
    }
424
425
    let args = BTreeMap::from([
426
        (WasmFeatureSet::MatrixSdkQrcode, ("crates/matrix-sdk-qrcode", "--features js")),
427
        (
428
            WasmFeatureSet::MatrixSdkNoDefault,
429
            ("crates/matrix-sdk", "--no-default-features --features js --lib"),
430
        ),
431
        (WasmFeatureSet::MatrixSdkBase, ("crates/matrix-sdk-base", "--features js")),
432
        (WasmFeatureSet::MatrixSdkCommon, ("crates/matrix-sdk-common", "--features js")),
433
        (
434
            WasmFeatureSet::MatrixSdkIndexeddbStoresNoCrypto,
435
            ("crates/matrix-sdk", "--no-default-features --features js,indexeddb --lib"),
436
        ),
437
        (
438
            WasmFeatureSet::MatrixSdkIndexeddbStores,
439
            (
440
                "crates/matrix-sdk",
441
                "--no-default-features --features js,indexeddb,e2e-encryption,testing --lib",
442
            ),
443
        ),
444
        (WasmFeatureSet::IndexeddbAllFeatures, ("crates/matrix-sdk-indexeddb", "")),
445
        (
446
            WasmFeatureSet::IndexeddbCrypto,
447
            ("crates/matrix-sdk-indexeddb", "--no-default-features --features e2e-encryption"),
448
        ),
449
        (
450
            WasmFeatureSet::IndexeddbState,
451
            ("crates/matrix-sdk-indexeddb", "--no-default-features --features state-store"),
452
        ),
453
    ]);
454
455
    let sh = sh();
456
    let run = |runner: WasmTestRunner, (folder, arg_set): (&str, &str)| {
457
        let _pwd = sh.push_dir(folder);
458
459
        cmd!(sh, "pwd").run()?; // print dir so we know what might have failed
460
461
        if matches!(runner, WasmTestRunner::All | WasmTestRunner::Node) {
462
            cmd!(sh, "wasm-pack test --node -- ")
463
                .args(arg_set.split_whitespace())
464
                .env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
465
                .run()?;
466
        }
467
468
        if matches!(runner, WasmTestRunner::All | WasmTestRunner::Firefox) {
469
            cmd!(sh, "wasm-pack test --firefox --headless --")
470
                .args(arg_set.split_whitespace())
471
                .env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
472
                .run()?;
473
        }
474
475
        if matches!(runner, WasmTestRunner::All | WasmTestRunner::Chrome) {
476
            cmd!(sh, "wasm-pack test --chrome --headless --")
477
                .args(arg_set.split_whitespace())
478
                .env(WASM_TIMEOUT_ENV_KEY, WASM_TIMEOUT_VALUE)
479
                .run()?;
480
        }
481
482
        Ok::<_, xshell::Error>(())
483
    };
484
485
    match cmd {
486
        Some(cmd) => {
487
            run(runner, args[&cmd])?;
488
        }
489
        None => {
490
            for &arg_set in args.values() {
491
                run(runner, arg_set)?;
492
            }
493
        }
494
    }
495
496
    Ok(())
497
}
498
499
fn run_coverage(output_format: CoverageOutputFormat) -> Result<()> {
500
    let sh = sh();
501
    let cmd = cmd!(sh, "rustup run stable cargo llvm-cov nextest");
502
    let cmd = cmd.args([
503
        "--workspace",
504
        "--exclude",
505
        "matrix-sdk-indexeddb",
506
        "--ignore-filename-regex",
507
        "testing/*|bindings/*|uniffi-bindgen|labs/*",
508
    ]);
509
510
    let cmd = match output_format {
511
        CoverageOutputFormat::Text => cmd,
512
        CoverageOutputFormat::Html => cmd.arg("--html"),
513
        CoverageOutputFormat::Codecov => {
514
            cmd.args(["--codecov", "--output-path", "coverage.xml", "--profile", "ci"])
515
        }
516
    };
517
518
    cmd.run()?;
519
520
    Ok(())
521
}