microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/apply-async-process-wait-functionality

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

flowey/flowey_cli/src/cli/regen.rs

212lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use anyhow::Context;
5use std::collections::BTreeMap;
6use std::path::Path;
7use std::path::PathBuf;
8
9/// Regenerate all pipelines defined in the repo's root `.flowey.toml`
10#[derive(clap::Args)]
11pub struct Regen {
12 /// Check that pipelines are up to date, without regenerating them.
13 #[clap(long)]
14 check: bool,
15
16 /// Pass `--quiet` to any subprocess invocations of `cargo run`.
17 #[clap(long)]
18 quiet: bool,
19}
20
21impl Regen {
22 pub fn run(self, repo_root: &Path) -> anyhow::Result<()> {
23 install_flowey_merge_driver()?;
24
25 if !repo_root.join(".flowey.toml").exists() {
26 log::warn!("no .flowey.toml exists in the repo root");
27 return Ok(());
28 }
29
30 let flowey_toml = fs_err::read_to_string(repo_root.join(".flowey.toml"))?;
31 let flowey_toml: flowey_toml::FloweyToml =
32 toml_edit::de::from_str(&flowey_toml).context("while parsing .flowey.toml")?;
33
34 let data = resolve_flowey_toml(flowey_toml, repo_root.to_owned())
35 .context("while resolving .flowey.toml")?;
36
37 let mut bin2flowey = BTreeMap::<String, PathBuf>::new();
38
39 let mut error = false;
40 for ResolvedFloweyToml {
41 working_dir,
42 pipelines,
43 } in data
44 {
45 for (bin_name, pipelines) in pipelines {
46 let exe_name = format!("{bin_name}{}", std::env::consts::EXE_SUFFIX);
47
48 let bin = if let Some(bin) = bin2flowey.get(&bin_name) {
49 bin.clone()
50 } else {
51 // build the requested flowey
52 {
53 let quiet = self.quiet.then_some("-q");
54 #[expect(
55 clippy::disallowed_methods,
56 reason = "not in a flowey runtime context"
57 )]
58 let sh = xshell::Shell::new()?;
59 sh.change_dir(&working_dir);
60 #[expect(clippy::disallowed_macros)]
61 xshell::cmd!(sh, "cargo build -p {bin_name} {quiet...}").run()?;
62 }
63
64 // find the built flowey
65 let bin = working_dir
66 .join(
67 std::env::var("CARGO_TARGET_DIR")
68 .as_deref()
69 .unwrap_or("target"),
70 )
71 .join(std::env::var("CARGO_BUILD_TARGET").as_deref().unwrap_or(""))
72 .join("debug")
73 .join(&exe_name);
74
75 if !bin.exists() {
76 panic!("should have found built {bin_name} at {}", bin.display());
77 }
78
79 // stash result for future consumers
80 bin2flowey.insert(bin_name.clone(), bin.clone());
81 bin
82 };
83
84 for (backend, defns) in pipelines {
85 for flowey_toml::PipelineDefn { file, cmd } in defns {
86 let check = if self.check {
87 vec!["--check".into(), file.display().to_string()]
88 } else {
89 vec![]
90 };
91
92 #[expect(
93 clippy::disallowed_methods,
94 reason = "not in a flowey runtime context"
95 )]
96 let sh = xshell::Shell::new()?;
97 sh.change_dir(&working_dir);
98 #[expect(clippy::disallowed_macros)]
99 let res = xshell::cmd!(
100 sh,
101 "{bin} pipeline {backend} --out {file} {check...} {cmd...}"
102 )
103 .run();
104
105 if res.is_err() {
106 error = true;
107 }
108 }
109 }
110 }
111 }
112
113 if error {
114 anyhow::bail!("encountered one or more errors")
115 }
116
117 Ok(())
118 }
119}
120
121#[derive(Debug)]
122pub struct ResolvedFloweyToml {
123 pub working_dir: PathBuf,
124 // (bin, (backend, metadata))
125 pub pipelines: BTreeMap<String, BTreeMap<String, Vec<flowey_toml::PipelineDefn>>>,
126}
127
128fn resolve_flowey_toml(
129 flowey_toml: flowey_toml::FloweyToml,
130 working_dir: PathBuf,
131) -> anyhow::Result<Vec<ResolvedFloweyToml>> {
132 let mut v = Vec::new();
133 resolve_flowey_toml_inner(flowey_toml, working_dir, &mut v)?;
134 Ok(v)
135}
136
137fn resolve_flowey_toml_inner(
138 flowey_toml: flowey_toml::FloweyToml,
139 working_dir: PathBuf,
140 resolved: &mut Vec<ResolvedFloweyToml>,
141) -> anyhow::Result<()> {
142 let flowey_toml::FloweyToml { include, pipeline } = flowey_toml;
143
144 let mut resolved_pipelines: BTreeMap<String, BTreeMap<String, Vec<_>>> = BTreeMap::new();
145 for (bin_name, pipelines) in pipeline {
146 for (backend, defns) in pipelines {
147 resolved_pipelines
148 .entry(bin_name.clone())
149 .or_default()
150 .entry(backend)
151 .or_default()
152 .extend(defns);
153 }
154 }
155
156 for path in include.unwrap_or_default() {
157 let path = working_dir.join(path);
158 let flowey_toml = fs_err::read_to_string(&path)?;
159 let flowey_toml: flowey_toml::FloweyToml = toml_edit::de::from_str(&flowey_toml)
160 .with_context(|| anyhow::anyhow!("while parsing {}", path.display()))?;
161 let mut working_dir = path;
162 working_dir.pop();
163 resolve_flowey_toml_inner(flowey_toml, working_dir, resolved)?
164 }
165
166 resolved.push(ResolvedFloweyToml {
167 working_dir,
168 pipelines: resolved_pipelines,
169 });
170
171 Ok(())
172}
173
174mod flowey_toml {
175 use serde::Deserialize;
176 use serde::Serialize;
177 use std::collections::BTreeMap;
178 use std::path::PathBuf;
179
180 #[derive(Debug, Serialize, Deserialize)]
181 pub struct FloweyToml {
182 pub include: Option<Vec<PathBuf>>,
183 // (bin, (backend, metadata))
184 pub pipeline: BTreeMap<String, BTreeMap<String, Vec<PipelineDefn>>>,
185 }
186
187 #[derive(Debug, Serialize, Deserialize)]
188 pub struct PipelineDefn {
189 pub file: PathBuf,
190 pub cmd: Vec<String>,
191 }
192}
193
194fn install_flowey_merge_driver() -> anyhow::Result<()> {
195 const DRIVER_NAME: &str = "flowey-theirs merge driver";
196 const DRIVER_COMMAND: &str = "cp %B %A";
197
198 #[expect(clippy::disallowed_methods, reason = "not in a flowey runtime context")]
199 let sh = xshell::Shell::new()?;
200 #[expect(clippy::disallowed_macros, reason = "not in a flowey runtime context")]
201 xshell::cmd!(sh, "git config merge.flowey-theirs.name {DRIVER_NAME}")
202 .quiet()
203 .ignore_status()
204 .run()?;
205 #[expect(clippy::disallowed_macros, reason = "not in a flowey runtime context")]
206 xshell::cmd!(sh, "git config merge.flowey-theirs.driver {DRIVER_COMMAND}")
207 .quiet()
208 .ignore_status()
209 .run()?;
210
211 Ok(())
212}
213