Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for Rust and wasm32-wasi on spin new and spin build #1432

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ edition = { workspace = true }

[dependencies]
anyhow = "1.0.57"
dialoguer = "0.10"
futures = "0.3.21"
is-terminal = "0.4"
rhai = "1.13.0"
serde = { version = "1.0", features = [ "derive" ] }
spin-loader = { path = "../loader" }
subprocess = "0.2.8"
Expand Down
7 changes: 7 additions & 0 deletions crates/build/src/interaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// TODO: subset of spin_templates::interaction

use dialoguer::Confirm;

pub(crate) fn confirm(text: &str) -> std::io::Result<bool> {
Confirm::new().with_prompt(text).interact()
}
58 changes: 53 additions & 5 deletions crates/build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

//! A library for building Spin components.

mod interaction;
mod manifest;
mod scripting;

use anyhow::{anyhow, bail, Context, Result};
use spin_loader::local::parent_dir;
Expand All @@ -24,18 +26,64 @@ pub async fn build(manifest_file: &Path) -> Result<()> {
return Ok(());
}

app.components
let results = app
.components
.into_iter()
.map(|c| build_component(c, &app_dir))
.collect::<Result<Vec<_>, _>>()?;
.map(|c| (c.clone(), build_component(&c, &app_dir)))
.collect::<Vec<_>>();

let mut fail_count = 0;
let mut checks = vec![];

for (c, br) in results {
if let Err(e) = br {
fail_count += 1;
if fail_count == 1 {
// Blank line before first summary line, others kept together
eprintln!();
}
eprintln!("{e:#}");

let build_dir = ".spinbuild";
if let Some(build) = &c.build {
if let Some(check) = &build.check {
let check = match &build.workdir {
None => app_dir.join(build_dir).join(check),
Some(wd) => app_dir.join(wd).join(build_dir).join(check),
};
if check.exists() {
checks.push(check);
}
}
}
}
}

if !checks.is_empty() {
let mut engine = rhai::Engine::new();
scripting::register_functions(&mut engine);
for check in checks {
// Because we have to pipe output directly to the console, we can't pass it to the script.
// The script will have to assume the worst.
let check_result = engine.run_file(check.clone());
if let Err(e) = check_result {
tracing::warn!("Check script error in {check:?}: {e:?}");
}
}
eprintln!(); // Because one of the checks might have printed something and we want to keep it apart from the Rust termination message
}

if fail_count > 0 {
bail!("Build failed for {fail_count} component(s)")
}

println!("Successfully ran the build command for the Spin components.");
Ok(())
}

/// Run the build command of the component.
fn build_component(raw: RawComponentManifest, app_dir: &Path) -> Result<()> {
match raw.build {
fn build_component(raw: &RawComponentManifest, app_dir: &Path) -> Result<()> {
match raw.build.as_ref() {
Some(b) => {
println!(
"Executing the build command for component {}: {}",
Expand Down
1 change: 1 addition & 0 deletions crates/build/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ pub(crate) struct RawBuildConfig {
pub command: String,
pub workdir: Option<PathBuf>,
pub watch: Option<Vec<String>>,
pub check: Option<PathBuf>,
}
84 changes: 84 additions & 0 deletions crates/build/src/scripting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// TODO: subset of spin_templates::scripting

use is_terminal::IsTerminal;

pub(crate) fn register_functions(engine: &mut rhai::Engine) {
engine.register_fn("ask_yn", ask_yn);
engine.register_fn("exec", exec);
engine.register_fn("interactive", interactive);
engine
.register_type::<CommandOutput>()
.register_get("program_found", CommandOutput::program_found)
.register_get("exit_code", CommandOutput::exit_code)
.register_get("stdout", CommandOutput::stdout)
.register_get("stderr", CommandOutput::stderr);
}

// Functions and types to be injected into the scripting engine

fn exec(
command: rhai::ImmutableString,
args: rhai::Array,
) -> Result<rhai::Dynamic, Box<rhai::EvalAltResult>> {
let command = command.to_string();
let args = args.iter().map(|item| item.to_string()).collect::<Vec<_>>();
let outputr = std::process::Command::new(command).args(args).output();

let output = match outputr {
Ok(output) => CommandOutput {
program_found: true,
exit_code: output.status.code().unwrap_or(-1),
stdout: String::from_utf8_lossy(&output.stdout).to_string(),
stderr: String::from_utf8_lossy(&output.stderr).to_string(),
},
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => CommandOutput {
program_found: false,
exit_code: -2,
stdout: "".to_owned(),
stderr: "".to_owned(),
},
_ => return Err(Box::<rhai::EvalAltResult>::from(e.to_string())),
},
};

Ok(rhai::Dynamic::from(output))
}

fn ask_yn(text: rhai::ImmutableString) -> bool {
if !std::io::stderr().is_terminal() {
eprintln!("Answering 'no' to '{text}'");
return false;
}
crate::interaction::confirm(text.as_ref()).unwrap_or(false)
}

fn interactive() -> bool {
std::io::stderr().is_terminal()
}

#[derive(Clone, Debug)]
struct CommandOutput {
program_found: bool,
exit_code: i32,
stdout: String,
stderr: String,
}

impl CommandOutput {
fn program_found(&mut self) -> bool {
self.program_found
}

fn exit_code(&mut self) -> i64 {
self.exit_code.into()
}

fn stdout(&mut self) -> String {
self.stdout.clone()
}

fn stderr(&mut self) -> String {
self.stderr.clone()
}
}
2 changes: 2 additions & 0 deletions crates/templates/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dirs = "3.0"
fs_extra = "1.2"
heck = "0.4"
indexmap = { version = "1", features = ["serde"] }
is-terminal = "0.4"
itertools = "0.10.3"
lazy_static = "1.4.0"
liquid = "0.23"
Expand All @@ -23,6 +24,7 @@ liquid-lib = "0.23"
path-absolutize = "3.0.13"
pathdiff = "0.2.1"
regex = "1.5.4"
rhai = "1.13.0"
semver = "1.0"
serde = { version = "1.0", features = [ "derive" ] }
sha2 = "0.10.1"
Expand Down
1 change: 1 addition & 0 deletions crates/templates/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod manager;
mod reader;
mod renderer;
mod run;
mod scripting;
mod source;
mod store;
mod template;
Expand Down
6 changes: 5 additions & 1 deletion crates/templates/src/reader.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::collections::{HashMap, HashSet};
use std::{
collections::{HashMap, HashSet},
path::PathBuf,
};

use anyhow::Context;
use indexmap::IndexMap;
Expand All @@ -23,6 +26,7 @@ pub(crate) struct RawTemplateManifestV1 {
pub add_component: Option<RawTemplateVariant>,
pub parameters: Option<IndexMap<String, RawParameter>>,
pub custom_filters: Option<Vec<RawCustomFilter>>,
pub scripts: Option<IndexMap<String, PathBuf>>,
}

#[derive(Debug, Deserialize)]
Expand Down
2 changes: 2 additions & 0 deletions crates/templates/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ impl Run {
.and_then(|t| t.render())
.and_then_async(|o| async move { o.write().await })
.await
.and_then_async(|_| self.template.after_instantiate())
.await
.err()
}

Expand Down
Loading