Skip to content

Commit a6478a5

Browse files
Merge pull request #1179 from mikrostew/event-args
Add `Args` event to hold `argv`
2 parents 2ae2dfe + fd3c6de commit a6478a5

File tree

9 files changed

+438
-46
lines changed

9 files changed

+438
-46
lines changed

crates/volta-core/src/event.rs

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33
use std::env;
44
use std::time::{SystemTime, UNIX_EPOCH};
55

6-
use serde::Serialize;
6+
use serde::{Deserialize, Serialize};
77

88
use crate::error::{ExitCode, VoltaError};
99
use crate::hook::Publish;
10-
use crate::monitor::Monitor;
10+
use crate::monitor::send_events;
1111
use crate::session::ActivityKind;
1212

1313
// the Event data that is serialized to JSON and sent the plugin
14-
#[derive(Serialize)]
14+
#[derive(Deserialize, Serialize)]
1515
pub struct Event {
1616
timestamp: u64,
17-
name: String,
18-
event: EventKind,
17+
pub name: String,
18+
pub event: EventKind,
1919
}
2020

21-
#[derive(Serialize)]
21+
#[derive(Deserialize, Serialize, PartialEq, Debug)]
2222
pub struct ErrorEnv {
2323
argv: String,
2424
exec_path: String,
@@ -27,9 +27,9 @@ pub struct ErrorEnv {
2727
platform_version: String,
2828
}
2929

30-
#[derive(Serialize)]
30+
#[derive(Deserialize, Serialize, PartialEq, Debug)]
3131
#[serde(rename_all = "lowercase")]
32-
enum EventKind {
32+
pub enum EventKind {
3333
Start,
3434
End {
3535
exit_code: i32,
@@ -42,6 +42,9 @@ enum EventKind {
4242
ToolEnd {
4343
exit_code: i32,
4444
},
45+
Args {
46+
argv: String,
47+
},
4548
}
4649

4750
impl EventKind {
@@ -122,6 +125,18 @@ impl EventLog {
122125
activity_kind,
123126
)
124127
}
128+
pub fn add_event_args(&mut self) {
129+
let argv = env::args_os()
130+
.enumerate()
131+
.fold(String::new(), |mut result, (i, arg)| {
132+
if i > 0 {
133+
result.push(' ');
134+
}
135+
result.push_str(&arg.to_string_lossy());
136+
result
137+
});
138+
self.add_event(EventKind::Args { argv }, ActivityKind::Args)
139+
}
125140

126141
fn add_event(&mut self, event_kind: EventKind, activity_kind: ActivityKind) {
127142
let event = event_kind.into_event(activity_kind);
@@ -133,8 +148,7 @@ impl EventLog {
133148
// Note: This call to unimplemented is left in, as it's not a Fallible operation that can use ErrorKind::Unimplemented
134149
Some(&Publish::Url(_)) => unimplemented!(),
135150
Some(&Publish::Bin(ref command)) => {
136-
let mut monitor = Monitor::new(command);
137-
monitor.send_events(&self.events);
151+
send_events(command, &self.events);
138152
}
139153
None => {}
140154
}
@@ -144,9 +158,10 @@ impl EventLog {
144158
#[cfg(test)]
145159
pub mod tests {
146160

147-
use super::EventLog;
161+
use super::{EventKind, EventLog};
148162
use crate::error::{ErrorKind, ExitCode};
149163
use crate::session::ActivityKind;
164+
use regex::Regex;
150165

151166
#[test]
152167
fn test_adding_events() {
@@ -156,18 +171,41 @@ pub mod tests {
156171
event_log.add_event_start(ActivityKind::Current);
157172
assert_eq!(event_log.events.len(), 1);
158173
assert_eq!(event_log.events[0].name, "current");
174+
assert_eq!(event_log.events[0].event, EventKind::Start);
159175

160176
event_log.add_event_end(ActivityKind::Pin, ExitCode::NetworkError);
161177
assert_eq!(event_log.events.len(), 2);
162178
assert_eq!(event_log.events[1].name, "pin");
179+
assert_eq!(event_log.events[1].event, EventKind::End { exit_code: 5 });
163180

164181
event_log.add_event_tool_end(ActivityKind::Version, 12);
165182
assert_eq!(event_log.events.len(), 3);
166183
assert_eq!(event_log.events[2].name, "version");
184+
assert_eq!(
185+
event_log.events[2].event,
186+
EventKind::ToolEnd { exit_code: 12 }
187+
);
167188

168189
let error = ErrorKind::BinaryExecError.into();
169190
event_log.add_event_error(ActivityKind::Install, &error);
170191
assert_eq!(event_log.events.len(), 4);
171192
assert_eq!(event_log.events[3].name, "install");
193+
// not checking the error because it has too much machine-specific info
194+
195+
event_log.add_event_args();
196+
assert_eq!(event_log.events.len(), 5);
197+
assert_eq!(event_log.events[4].name, "args");
198+
match event_log.events[4].event {
199+
EventKind::Args { ref argv } => {
200+
let re = Regex::new("volta_core").unwrap();
201+
assert!(re.is_match(argv));
202+
}
203+
_ => {
204+
panic!(
205+
"Expected EventKind::Args {{ argv }}, Got: {:?}",
206+
event_log.events[4].event
207+
);
208+
}
209+
}
172210
}
173211
}

crates/volta-core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
mod command;
44
pub mod error;
5-
mod event;
5+
pub mod event;
66
pub mod fs;
77
mod hook;
88
pub mod inventory;

crates/volta-core/src/monitor.rs

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,82 @@
1+
use std::env;
12
use std::io::Write;
3+
use std::path::PathBuf;
24
use std::process::{Child, Stdio};
35

46
use log::debug;
7+
use tempfile::NamedTempFile;
58

69
use crate::command::create_command;
710
use crate::event::Event;
811

9-
pub struct Monitor {
10-
monitor_process: Option<Child>,
11-
}
12-
13-
impl Monitor {
14-
/// Returns the current monitor.
15-
pub fn new(command: &str) -> Monitor {
16-
Monitor {
17-
monitor_process: spawn_process(command),
12+
/// Send event to the spawned command process
13+
// if hook command is not configured, this is not called
14+
pub fn send_events(command: &str, events: &[Event]) {
15+
match serde_json::to_string_pretty(&events) {
16+
Ok(events_json) => {
17+
let tempfile_path = env::var_os("VOLTA_WRITE_EVENTS_FILE")
18+
.and_then(|_| write_events_file(events_json.clone()));
19+
if let Some(ref mut child_process) = spawn_process(command, tempfile_path) {
20+
if let Some(ref mut p_stdin) = child_process.stdin.as_mut() {
21+
if let Err(error) = writeln!(p_stdin, "{}", events_json) {
22+
debug!("Could not write events to executable stdin: {:?}", error);
23+
}
24+
}
25+
}
26+
}
27+
Err(error) => {
28+
debug!("Could not serialize events data to JSON: {:?}", error);
1829
}
1930
}
31+
}
2032

21-
/// send event to the monitor process
22-
// if hook command is not configured, this is a no-op
23-
pub fn send_events(&mut self, events: &[Event]) {
24-
if let Some(ref mut child_process) = self.monitor_process {
25-
if let Some(ref mut p_stdin) = child_process.stdin.as_mut() {
26-
let json = serde_json::to_string(&events);
27-
28-
match json {
29-
Ok(data) => {
30-
// FIXME: tighten up this error message
31-
write!(p_stdin, "{}", data).expect("Writing data to plugin failed!");
32-
}
33-
Err(error) => {
34-
// FIXME: tighten up this error message
35-
debug!("There was a problem serializing the JSON data: {:?}", error);
33+
// Write the events JSON to a file in the temporary directory
34+
fn write_events_file(events_json: String) -> Option<PathBuf> {
35+
match NamedTempFile::new() {
36+
Ok(mut events_file) => {
37+
match events_file.write_all(events_json.as_bytes()) {
38+
Ok(()) => {
39+
let path = events_file.into_temp_path();
40+
// if it's not persisted, the temp file will be automatically deleted
41+
// (and the executable won't be able to read it)
42+
match path.keep() {
43+
Ok(tempfile_path) => Some(tempfile_path),
44+
Err(error) => {
45+
debug!("Failed to persist temp file for events data: {:?}", error);
46+
None
47+
}
3648
}
37-
};
49+
}
50+
Err(error) => {
51+
debug!("Failed to write events to the temp file: {:?}", error);
52+
None
53+
}
3854
}
3955
}
56+
Err(error) => {
57+
debug!("Failed to create a temp file for events data: {:?}", error);
58+
None
59+
}
4060
}
4161
}
4262

43-
fn spawn_process(command: &str) -> Option<Child> {
63+
// Spawn a child process to receive the events data, setting the path to the events file as an env var
64+
fn spawn_process(command: &str, tempfile_path: Option<PathBuf>) -> Option<Child> {
4465
command.split(' ').take(1).next().and_then(|executable| {
4566
let mut child = create_command(executable);
4667
child.args(command.split(' ').skip(1));
4768
child.stdin(Stdio::piped());
69+
if let Some(events_file) = tempfile_path {
70+
child.env("EVENTS_FILE", events_file);
71+
}
4872

4973
#[cfg(not(debug_assertions))]
5074
// Hide stdout and stderr of spawned process in release mode
5175
child.stdout(Stdio::null()).stderr(Stdio::null());
5276

5377
match child.spawn() {
5478
Err(err) => {
55-
debug!("Unable to run plugin command: '{}'\n{}", command, err);
79+
debug!("Unable to run executable command: '{}'\n{}", command, err);
5680
None
5781
}
5882
Ok(c) => Some(c),

crates/volta-core/src/session.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub enum ActivityKind {
3636
Which,
3737
Setup,
3838
Run,
39+
Args,
3940
}
4041

4142
impl Display for ActivityKind {
@@ -62,6 +63,7 @@ impl Display for ActivityKind {
6263
ActivityKind::Completions => "completions",
6364
ActivityKind::Which => "which",
6465
ActivityKind::Run => "run",
66+
ActivityKind::Args => "args",
6567
};
6668
f.write_str(s)
6769
}
@@ -145,12 +147,20 @@ impl Session {
145147
}
146148

147149
fn publish_to_event_log(self) {
148-
let plugin_res = self
149-
.hooks()
150+
let Self {
151+
project,
152+
hooks,
153+
mut event_log,
154+
..
155+
} = self;
156+
let plugin_res = project
157+
.get()
158+
.and_then(|p| hooks.get(p))
150159
.map(|hooks| hooks.events().and_then(|e| e.publish.as_ref()));
151160
match plugin_res {
152161
Ok(plugin) => {
153-
self.event_log.publish(plugin);
162+
event_log.add_event_args();
163+
event_log.publish(plugin);
154164
}
155165
Err(e) => {
156166
debug!("Unable to publish event log.\n{}", e);

0 commit comments

Comments
 (0)