fluentci_shared/
shared.rs

1use anyhow::Error;
2use extism::{convert::Json, host_fn};
3use extism::{Manifest, PluginBuilder, Wasm, PTR};
4use fluentci_common::common;
5use fluentci_ext::devbox::Devbox as DevboxExt;
6use fluentci_ext::devenv::Devenv as DevenvExt;
7use fluentci_ext::envhub::Envhub as EnvhubExt;
8use fluentci_ext::flox::Flox as FloxExt;
9use fluentci_ext::git::Git as GitExt;
10use fluentci_ext::hermit::Hermit as HermitExt;
11use fluentci_ext::http::Http as HttpExt;
12use fluentci_ext::mise::Mise as MiseExt;
13use fluentci_ext::nix::Nix as NixExt;
14use fluentci_ext::pixi::Pixi as PixiExt;
15use fluentci_ext::pkgx::Pkgx as PkgxExt;
16use fluentci_ext::runner::Runner as RunnerExt;
17use fluentci_ext::Extension;
18use fluentci_secrets::Provider;
19use fluentci_types::cache::Cache;
20use fluentci_types::file::File;
21use fluentci_types::Module;
22use std::sync::Arc;
23
24use crate::{
25    cache::*, devbox::*, devenv::*, directory::*, envhub::*, file::*, flox::*, git::*, hermit::*,
26    http::*, mise::*, nix::*, pipeline::*, pixi::*, pkgx::*, state::State,
27};
28
29host_fn!(pub set_runner(user_data: State; runner: String) {
30  let state = user_data.get()?;
31  let mut state = state.lock().unwrap();
32  state.runner = runner;
33  Ok(())
34});
35
36host_fn!(pub with_exec(user_data: State; args: Json<Vec<String>>) {
37  let state = user_data.get()?;
38  let state = state.lock().unwrap();
39  let graph = state.graph.clone();
40  let runner = state.runner.clone();
41  let runner: Arc<Box<dyn Extension + Send + Sync>> = match runner.as_str() {
42    "nix" => Arc::new(Box::new(NixExt::default())),
43    "devbox" => Arc::new(Box::new(DevboxExt::default())),
44    "devenv" => Arc::new(Box::new(DevenvExt::default())),
45    "envhub" => Arc::new(Box::new(EnvhubExt::default())),
46    "flox" => Arc::new(Box::new(FloxExt::default())),
47    "git" => Arc::new(Box::new(GitExt::default())),
48    "http" => Arc::new(Box::new(HttpExt::default())),
49    "mise" => Arc::new(Box::new(MiseExt::default())),
50    "runner" => Arc::new(Box::new(RunnerExt::default())),
51    "pixi" => Arc::new(Box::new(PixiExt::default())),
52    "pkgx" => Arc::new(Box::new(PkgxExt::default())),
53    "hermit" => Arc::new(Box::new(HermitExt::default())),
54    _ => Arc::new(Box::new(RunnerExt::default())),
55  };
56  common::with_exec(graph, args.into_inner(), runner)?;
57  Ok(())
58});
59
60host_fn!(pub with_workdir(user_data: State; path: String) {
61  let state = user_data.get()?;
62  let state = state.lock().unwrap();
63  let graph = state.graph.clone();
64  let runner = state.runner.clone();
65  let runner: Arc<Box<dyn Extension + Send + Sync>> = match runner.as_str() {
66    "nix" => Arc::new(Box::new(NixExt::default())),
67    "devbox" => Arc::new(Box::new(DevboxExt::default())),
68    "devenv" => Arc::new(Box::new(DevenvExt::default())),
69    "envhub" => Arc::new(Box::new(EnvhubExt::default())),
70    "flox" => Arc::new(Box::new(FloxExt::default())),
71    "git" => Arc::new(Box::new(GitExt::default())),
72    "http" => Arc::new(Box::new(HttpExt::default())),
73    "mise" => Arc::new(Box::new(MiseExt::default())),
74    "runner" => Arc::new(Box::new(RunnerExt::default())),
75    "pixi" => Arc::new(Box::new(PixiExt::default())),
76    "pkgx" => Arc::new(Box::new(PkgxExt::default())),
77    "hermit" => Arc::new(Box::new(HermitExt::default())),
78    _ => Arc::new(Box::new(RunnerExt::default())),
79  };
80  common::with_workdir(graph, path, runner)?;
81  Ok(())
82});
83
84host_fn!(pub with_cache(user_data: State;  cache: Json<Cache>) {
85  let state = user_data.get()?;
86  let state = state.lock().unwrap();
87  let graph = state.graph.clone();
88  let cache = cache.into_inner();
89  common::with_cache(graph, cache.id, cache.path)?;
90  Ok(())
91});
92
93host_fn!(pub with_file(user_data: State;  file: Json<File>) {
94  let state = user_data.get()?;
95  let state = state.lock().unwrap();
96  let graph = state.graph.clone();
97  let file = file.into_inner();
98  common::with_file(graph, file.id, file.path)?;
99  Ok(())
100});
101
102host_fn!(pub with_service(user_data: State; service_id: String) {
103  let state = user_data.get()?;
104  let state = state.lock().unwrap();
105  let graph = state.graph.clone();
106  common::with_service(graph, service_id)?;
107  Ok(())
108});
109
110host_fn!(pub stdout(user_data: State;) -> String {
111  let state = user_data.get()?;
112  let state = state.lock().unwrap();
113  let graph = state.graph.clone();
114  let rx = state.rx.clone();
115  common::stdout(graph, rx)
116});
117
118host_fn!(pub stderr(user_data: State;) -> String {
119  let state = user_data.get()?;
120  let state = state.lock().unwrap();
121  let graph = state.graph.clone();
122  let rx = state.rx.clone();
123  common::stderr(graph, rx)
124});
125
126host_fn!(pub zip(user_data: State; path: String) -> Json<File> {
127  let state = user_data.get()?;
128  let state = state.lock().unwrap();
129  let graph = state.graph.clone();
130  match common::zip(graph, path) {
131    Ok(file) => Ok(Json(File::from(file))),
132    Err(e) => Err(e),
133  }
134});
135
136host_fn!(pub tar_czvf(user_data: State; path: String) -> Json<File> {
137  let state = user_data.get()?;
138  let state = state.lock().unwrap();
139  let graph = state.graph.clone();
140  match common::tar_czvf(graph, path) {
141    Ok(file) => Ok(Json(File::from(file))),
142    Err(e) => Err(e),
143  }
144});
145
146host_fn!(pub get_env(user_data: State; key: String) -> String {
147  Ok(std::env::var(key).unwrap_or_default())
148});
149
150host_fn!(pub set_envs(user_data: State; env: Json<Vec<(String, String)>>) {
151  for (key, value) in env.into_inner() {
152    std::env::set_var(key, value);
153  }
154  Ok(())
155});
156
157host_fn!(pub remove_env(user_data: State; key: String) {
158  std::env::remove_var(key);
159  Ok(())
160});
161
162host_fn!(pub has_env(user_data: State; key: String) -> Json<bool> {
163  Ok(Json(std::env::var(key).is_ok()))
164});
165
166host_fn!(pub get_os(user_data: State;) -> String {
167  Ok(std::env::consts::OS.to_string())
168});
169
170host_fn!(pub get_arch(user_data: State;) -> String {
171  Ok(std::env::consts::ARCH.to_string())
172});
173
174host_fn!(pub as_service(user_data: State; name: String) -> Json<Service> {
175  let state = user_data.get()?;
176  let state = state.lock().unwrap();
177  let graph = state.graph.clone();
178  let service = common::as_service(graph, name)?;
179  Ok(Json(service))
180});
181
182host_fn!(pub call(user_data: State; opts: Json<Module>) -> String {
183    let opts = opts.into_inner();
184    let module = opts.url.clone();
185    let module = match module.starts_with("http") {
186        true => Wasm::url(module),
187        false => Wasm::file(module),
188    };
189
190    let manifest = Manifest::new([module]);
191    let mut plugin = PluginBuilder::new(manifest.clone())
192        .with_wasi(true)
193        .with_function("set_runner", [PTR], [], user_data.clone(), set_runner)
194        .with_function("cache", [PTR], [PTR], user_data.clone(), cache)
195        .with_function("devbox", [], [PTR], user_data.clone(), devbox)
196        .with_function("devenv", [], [PTR], user_data.clone(), devenv)
197        .with_function("directory", [PTR], [PTR], user_data.clone(), directory)
198        .with_function("entries", [PTR], [PTR], user_data.clone(), entries)
199        .with_function("envhub", [], [PTR], user_data.clone(), envhub)
200        .with_function("unzip", [PTR], [PTR], user_data.clone(), unzip)
201        .with_function("file", [PTR], [PTR], user_data.clone(), file)
202        .with_function("flox", [], [PTR], user_data.clone(), flox)
203        .with_function("git", [PTR], [PTR], user_data.clone(), git)
204        .with_function("branch", [PTR], [], user_data.clone(), branch)
205        .with_function("commit", [], [PTR], user_data.clone(), commit)
206        .with_function("tree", [], [PTR], user_data.clone(), tree)
207        .with_function("http", [PTR], [PTR], user_data.clone(), http)
208        .with_function("nix", [PTR], [PTR], user_data.clone(), nix)
209        .with_function("pipeline", [PTR], [PTR], user_data.clone(), pipeline)
210        .with_function("pixi", [], [PTR], user_data.clone(), pixi)
211        .with_function("pkgx", [], [PTR], user_data.clone(), pkgx)
212        .with_function("mise", [], [PTR], user_data.clone(), mise)
213        .with_function("trust", [PTR], [], user_data.clone(), trust)
214        .with_function("with_exec", [PTR], [], user_data.clone(), with_exec)
215        .with_function("with_workdir", [PTR], [], user_data.clone(), with_workdir)
216        .with_function("with_cache", [PTR], [], user_data.clone(), with_cache)
217        .with_function("stdout", [], [PTR], user_data.clone(), stdout)
218        .with_function("stderr", [], [PTR], user_data.clone(), stderr)
219        .with_function("zip", [PTR], [PTR], user_data.clone(), zip)
220        .with_function("tar_czvf", [PTR], [PTR], user_data.clone(), tar_czvf)
221        .with_function("tar_xzvf", [PTR], [PTR], user_data.clone(), tar_xzvf)
222        .with_function("md5", [PTR], [PTR], user_data.clone(), md5)
223        .with_function("sha256", [PTR], [PTR], user_data.clone(), sha256)
224        .with_function("chmod", [PTR], [PTR], user_data.clone(), chmod)
225        .with_function("with_file", [PTR], [], user_data.clone(), with_file)
226        .with_function("get_env", [PTR], [PTR], user_data.clone(), get_env)
227        .with_function("set_envs", [PTR], [], user_data.clone(), set_envs)
228        .with_function("remove_env", [PTR], [], user_data.clone(), remove_env)
229        .with_function("has_env", [PTR], [PTR], user_data.clone(), has_env)
230        .with_function("get_os", [], [PTR], user_data.clone(), get_os)
231        .with_function("get_arch", [], [PTR], user_data.clone(), get_arch)
232        .with_function("call", [PTR], [PTR], user_data.clone(), call)
233        .with_function("with_packages", [PTR], [], user_data.clone(), with_packages)
234        .with_function("as_service", [PTR], [PTR], user_data.clone(), as_service)
235        .with_function("with_service", [PTR], [], user_data.clone(), with_service)
236        .with_function("wait_on", [PTR], [], user_data.clone(), wait_on)
237        .with_function("add_secretmanager", [PTR], [PTR], user_data.clone(), add_secretmanager)
238        .with_function("get_secret", [PTR], [PTR], user_data.clone(), get_secret)
239        .with_function("set_secret", [PTR], [PTR], user_data.clone(), set_secret)
240        .with_function("with_secret_variable", [PTR], [], user_data.clone(), with_secret_variable)
241        .with_function("get_secret_plaintext", [PTR], [PTR], user_data.clone(), get_secret_plaintext)
242        .with_function("hermit", [], [PTR], user_data.clone(), hermit)
243        .with_function("install", [], [], user_data.clone(), install)
244        .build()
245        .unwrap();
246
247      let func = opts.function.clone();
248      let args = opts.args.clone();
249      let result = plugin.call::<&str, &str>(func, &args)?;
250      Ok(result.to_string())
251});
252
253host_fn!(pub wait_on(user_data: State; args: Json<Vec<u32>>) {
254  let state = user_data.get()?;
255  let state = state.lock().unwrap();
256  let graph = state.graph.clone();
257  let args = args.into_inner();
258
259  if args.len() == 1 {
260    common::wait_on(graph, args[0], None)?;
261    return Ok(())
262  }
263  if args.len() >= 2 {
264    common::wait_on(graph, args[0], Some(args[1]))?;
265  }
266  Ok(())
267});
268
269host_fn!(pub add_secretmanager(user_data: State; provider: Json<Provider>) -> String {
270  let state = user_data.get()?;
271  let state = state.lock().unwrap();
272  let graph = state.graph.clone();
273  let provider = provider.into_inner();
274  let id = common::add_secretmanager(graph, provider)?;
275  Ok(id)
276});
277
278host_fn!(pub get_secret(user_data: State; params: Json<Vec<String>>) -> Json<Vec<Secret>> {
279  let state = user_data.get()?;
280  let state = state.lock().unwrap();
281  let graph = state.graph.clone();
282  let params = params.into_inner();
283
284  if params.len() != 2 {
285    return Err(Error::msg("Invalid number of arguments"));
286  }
287
288  let secret = common::get_secret(graph, &params[0], &params[1])?;
289  Ok(Json(secret))
290});
291
292host_fn!(pub with_secret_variable(user_data: State; params: Json<Vec<String>>) {
293  let state = user_data.get()?;
294  let state = state.lock().unwrap();
295  let graph = state.graph.clone();
296  let params = params.into_inner();
297  if params.len() != 2 {
298    return Err(Error::msg("Invalid number of arguments"));
299  }
300  let g = graph.lock().unwrap();
301  let secret_name = g.secret_names.get(&params[1]).unwrap();
302  let secret_name = secret_name.clone();
303  drop(g);
304
305  common::with_secret_variable(graph, &params[0], &params[1], &secret_name)?;
306  Ok(())
307});
308
309host_fn!(pub set_secret(user_data: State; params: Json<Vec<String>>) -> String {
310  let state = user_data.get()?;
311  let state = state.lock().unwrap();
312  let graph = state.graph.clone();
313  let params = params.into_inner();
314
315  if params.len() != 2 {
316    return Err(Error::msg("Invalid number of arguments"));
317  }
318
319  let secret_id = common::set_secret(graph, &params[0], &params[1])?;
320  Ok(secret_id)
321});
322
323host_fn!(pub get_secret_plaintext(user_data: State; params: Json<Vec<String>>) -> String {
324  let state = user_data.get()?;
325  let state = state.lock().unwrap();
326  let graph = state.graph.clone();
327  let params = params.into_inner();
328
329  if params.len() != 2 {
330    return Err(Error::msg("Invalid number of arguments"));
331  }
332
333  let secret = common::get_secret_plaintext(graph, &params[0], &params[1])?;
334  Ok(secret)
335});