xtask_lib/tasks/ci/matrix.rs
1//! `cargo xtask ci matrix` — emit GitHub Actions dynamic matrix JSON.
2//!
3//! Scans the workspace `Cargo.toml` files for feature flags and writes two
4//! outputs to `$GITHUB_OUTPUT`:
5//!
6//! - `matrix` — feature × OS entries (for the `clippy` job)
7//! - `test-matrix` — feature entries for ubuntu test jobs
8//!
9//! ```text
10//! matrix={"include":[{"label":"default","features":"","os":"ubuntu-latest"},…]}
11//! test-matrix={"include":[{"label":"default","features":"","os":"ubuntu-latest"},…]}
12//! ```
13//!
14//! ## Usage in a workflow
15//!
16//! ```yaml
17//! generate-matrix:
18//! outputs:
19//! matrix: ${{ steps.matrix.outputs.matrix }}
20//! test-matrix: ${{ steps.matrix.outputs.test-matrix }}
21//! steps:
22//! - id: matrix
23//! run: cargo xtask ci matrix
24//!
25//! clippy:
26//! needs: generate-matrix
27//! strategy:
28//! matrix:
29//! include: ${{ fromJson(needs.generate-matrix.outputs.matrix).include }}
30//!
31//! test:
32//! needs: generate-matrix
33//! strategy:
34//! matrix:
35//! include: ${{ fromJson(needs.generate-matrix.outputs.test-matrix).include }}
36//! ```
37
38use std::io::Write;
39
40use anyhow::{Context, Result};
41
42use crate::tasks::util::{matrix, workspace};
43
44/// Generates the feature matrix and writes both outputs to `$GITHUB_OUTPUT`.
45///
46/// # Errors
47///
48/// Returns an error if the workspace cannot be scanned, JSON serialisation
49/// fails, or `$GITHUB_OUTPUT` cannot be written.
50pub fn run() -> Result<()> {
51 let root = workspace::root()?;
52 let entries = matrix::scan(&root, matrix::CI_CLIPPY_OS)?;
53
54 let clippy_json = matrix::to_github_matrix_json(&entries)?;
55 write_github_output("matrix", &clippy_json)?;
56
57 let test_json = matrix::to_github_test_matrix_json(&entries)?;
58 write_github_output("test-matrix", &test_json)
59}
60
61fn write_github_output(key: &str, value: &str) -> Result<()> {
62 match std::env::var("GITHUB_OUTPUT") {
63 Ok(path) => {
64 let mut file = std::fs::OpenOptions::new()
65 .append(true)
66 .open(&path)
67 .with_context(|| format!("failed to open GITHUB_OUTPUT at {path}"))?;
68 writeln!(file, "{key}={value}")
69 .with_context(|| format!("failed to write to GITHUB_OUTPUT at {path}"))
70 }
71 Err(_) => {
72 println!("{value}");
73 Ok(())
74 }
75 }
76}