gha_lint/
lib.rs

1use std::path::Path;
2
3mod checks;
4mod diagnostics;
5mod discovery;
6mod workflow;
7
8pub use diagnostics::{Diagnostic, DiagnosticLevel};
9
10//library entrypoint used by CLI and potential embedding
11pub fn run_analysis(repo_root: &Path) -> anyhow::Result<Vec<Diagnostic>> {
12	//collect workflow files deterministically
13	let workflow_files = discovery::discover_workflows(repo_root)?;
14
15	//parse all workflows first
16	let mut parsed = Vec::new();
17	for wf_path in &workflow_files {
18		match workflow::ParsedWorkflow::from_file(wf_path) {
19			Ok(wf) => parsed.push(wf),
20			Err(err) => {
21				//yaml parse errors are surfaced as errors with the file path
22				parsed.push(workflow::ParsedWorkflow::from_parse_error(
23					wf_path.clone(),
24					err,
25				));
26			}
27		}
28	}
29
30	//run checks
31	let mut diags: Vec<Diagnostic> = Vec::new();
32
33	for wf in &parsed {
34		diags.extend(checks::missing_on::check(wf));
35		diags.extend(checks::missing_jobs::check(wf));
36		diags.extend(checks::invalid_needs::check(wf));
37		diags.extend(checks::invalid_uses::check(wf));
38		diags.extend(checks::rust_toolchain::check(wf));
39		diags.extend(checks::missing_checkout::check(wf));
40		diags.extend(checks::working_directory::check(wf, repo_root));
41		diags.extend(checks::file_references::check(wf, repo_root));
42	}
43
44	Ok(diags)
45}