Skip to content

Commit 44d1547

Browse files
authored
Merge pull request #3340 from itowlson/watchexec-aargh
Upgrade watchexec to 8.x and other audit adventures
2 parents 9117298 + dddb8df commit 44d1547

File tree

9 files changed

+714
-755
lines changed

9 files changed

+714
-755
lines changed

Cargo.lock

Lines changed: 599 additions & 660 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ bytes = { workspace = true }
2222
clap = { workspace = true, features = ["deprecated", "derive", "env"] }
2323
clearscreen = "4"
2424
comfy-table = "7"
25-
command-group = "2"
25+
command-group = { version = "5", features = ["with-tokio"] }
2626
ctrlc = { workspace = true }
2727
dialoguer = { workspace = true }
2828
futures = { workspace = true }
@@ -49,8 +49,9 @@ toml = { workspace = true }
4949
tracing = { workspace = true }
5050
url = { workspace = true }
5151
uuid = { version = "1.0", features = ["v4"] }
52-
watchexec = { git = "https://github.com/watchexec/watchexec.git", rev = "8e91d26ef6400c1e60b32a8314cbb144fa33f288" }
53-
watchexec-filterer-globset = { git = "https://github.com/watchexec/watchexec.git", rev = "8e91d26ef6400c1e60b32a8314cbb144fa33f288" }
52+
watchexec = "8.0"
53+
watchexec-events = "6.0"
54+
watchexec-filterer-globset = "8.0"
5455

5556
spin-app = { path = "crates/app" }
5657
spin-build = { path = "crates/build" }
@@ -170,7 +171,7 @@ toml = "0.8"
170171
toml_edit = "0.22"
171172
tower-service = "0.3.3"
172173
tracing = { version = "0.1.41", features = ["log"] }
173-
url = "2"
174+
url = "2.5.7"
174175
walkdir = "2"
175176
wasm-encoder = "0.239.0"
176177
wasm-metadata = "0.239.0"

crates/oci/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ edition = { workspace = true }
77
[dependencies]
88
anyhow = { workspace = true }
99
async-compression = { version = "0.4", features = ["gzip", "tokio"] }
10-
async-tar = "0.5"
1110
base64 = { workspace = true }
1211
chrono = { workspace = true }
1312
# Fork with updated auth to support ACR login
1413
# Ref https://github.com/camallo/dkregistry-rs/pull/263
1514
dirs = { workspace = true }
1615
dkregistry = { git = "https://github.com/fermyon/dkregistry-rs", rev = "161cf2b66996ed97c7abaf046e38244484814de3" }
1716
docker_credential = "1"
17+
flate2 = { workspace = true }
1818
futures-util = { workspace = true }
1919
itertools = { workspace = true }
2020
oci-distribution = { git = "https://github.com/fermyon/oci-distribution", rev = "7b291a39f74d1a3c9499d934a56cae6580fc8e37" }
@@ -25,6 +25,7 @@ spin-common = { path = "../common" }
2525
spin-compose = { path = "../compose" }
2626
spin-loader = { path = "../loader" }
2727
spin-locked-app = { path = "../locked-app" }
28+
tar = "0.4"
2829
tempfile = { workspace = true }
2930
tokio = { workspace = true, features = ["fs"] }
3031
tokio-util = { version = "0.7", features = ["compat"] }

crates/oci/src/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ impl Client {
447447
// Only add the archive layer to the OCI manifest
448448
tracing::trace!("Adding archive layer for all files in source {:?}", &source);
449449
let working_dir = tempfile::tempdir()?;
450-
let archive_path = crate::utils::archive(source, &working_dir.into_path())
450+
let archive_path = crate::utils::archive(source, &working_dir.keep())
451451
.await
452452
.context(format!(
453453
"Unable to create compressed archive for source {source:?}"

crates/oci/src/utils.rs

Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,59 @@
11
//! Utilities related to distributing Spin apps via OCI registries
22
33
use anyhow::{Context, Result};
4-
use async_compression::tokio::bufread::GzipDecoder;
5-
use async_compression::tokio::write::GzipEncoder;
6-
use async_tar::Archive;
4+
use flate2::read::GzDecoder;
5+
use flate2::write::GzEncoder;
76
use spin_common::ui::quoted_path;
87
use std::path::{Path, PathBuf};
8+
use tar::Archive;
99

1010
/// Create a compressed archive of source, returning its path in working_dir
1111
pub async fn archive(source: &Path, working_dir: &Path) -> Result<PathBuf> {
12-
// Create tar archive file
13-
let tar_gz_path = working_dir
14-
.join(source.file_name().unwrap())
15-
.with_extension("tar.gz");
16-
let tar_gz = tokio::fs::File::create(tar_gz_path.as_path())
17-
.await
18-
.context(format!(
12+
let source = source.to_owned();
13+
let working_dir = working_dir.to_owned();
14+
15+
tokio::task::spawn_blocking(move || {
16+
// Create tar archive file
17+
let tar_gz_path = working_dir
18+
.join(source.file_name().unwrap())
19+
.with_extension("tar.gz");
20+
let tar_gz = std::fs::File::create(tar_gz_path.as_path()).context(format!(
1921
"Unable to create tar archive for source {}",
20-
quoted_path(source)
22+
quoted_path(&source)
2123
))?;
2224

23-
// Create encoder
24-
// TODO: use zstd? May be more performant
25-
let tar_gz_enc = GzipEncoder::new(tar_gz);
26-
27-
// Build tar archive
28-
let mut tar_builder = async_tar::Builder::new(
29-
tokio_util::compat::TokioAsyncWriteCompatExt::compat_write(tar_gz_enc),
30-
);
31-
tar_builder
32-
.append_dir_all(".", source)
33-
.await
34-
.context(format!(
25+
// Create encoder
26+
// TODO: use zstd? May be more performant
27+
let tar_gz_enc = GzEncoder::new(tar_gz, flate2::Compression::default());
28+
29+
// Build tar archive
30+
let mut tar_builder = tar::Builder::new(tar_gz_enc);
31+
tar_builder.append_dir_all(".", &source).context(format!(
3532
"Unable to create tar archive for source {}",
36-
quoted_path(source)
33+
quoted_path(&source)
3734
))?;
38-
// Finish writing the archive
39-
tar_builder.finish().await?;
40-
// Shutdown the encoder
41-
use tokio::io::AsyncWriteExt;
42-
tar_builder
43-
.into_inner()
44-
.await?
45-
.into_inner()
46-
.shutdown()
47-
.await?;
48-
Ok(tar_gz_path)
35+
36+
// Finish writing the archive and shut down the encoder.
37+
let inner_enc = tar_builder.into_inner()?;
38+
inner_enc.finish()?;
39+
40+
Ok(tar_gz_path)
41+
})
42+
.await?
4943
}
5044

5145
/// Unpack a compressed archive existing at source into dest
5246
pub async fn unarchive(source: &Path, dest: &Path) -> Result<()> {
53-
let decoder = GzipDecoder::new(tokio::io::BufReader::new(
54-
tokio::fs::File::open(source).await?,
55-
));
56-
let archive = Archive::new(tokio_util::compat::TokioAsyncReadCompatExt::compat(decoder));
57-
if let Err(e) = archive.unpack(dest).await {
58-
return Err(e.into());
59-
};
60-
Ok(())
47+
let source = source.to_owned();
48+
let dest = dest.to_owned();
49+
50+
tokio::task::spawn_blocking(move || {
51+
let decoder = GzDecoder::new(std::fs::File::open(&source)?);
52+
let mut archive = Archive::new(decoder);
53+
if let Err(e) = archive.unpack(&dest) {
54+
return Err(e.into());
55+
};
56+
Ok(())
57+
})
58+
.await?
6159
}

src/commands/watch.rs

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ pub struct RuntimeConfigFactory {
279279
}
280280

281281
impl RuntimeConfigFactory {
282-
async fn build_config(&self) -> anyhow::Result<watchexec::config::RuntimeConfig> {
282+
async fn build_config(&self, rt: &watchexec::Config) -> anyhow::Result<()> {
283283
let manifest_str = tokio::fs::read_to_string(&self.manifest_file).await?;
284284
let manifest = spin_manifest::manifest_from_str(&manifest_str)?;
285285
let filterer = self
@@ -289,12 +289,11 @@ impl RuntimeConfigFactory {
289289

290290
let handler = NotifyOnFileChange::new(self.notifier.clone(), self.impact_description);
291291

292-
let mut rt = watchexec::config::RuntimeConfig::default();
293-
rt.pathset([&self.manifest_dir]);
292+
rt.pathset([self.manifest_dir.as_path()]);
294293
rt.filterer(filterer);
295-
rt.action_throttle(self.debounce);
296-
rt.on_action(handler);
297-
Ok(rt)
294+
rt.throttle(self.debounce);
295+
rt.on_action(move |ah| handler.handle(ah));
296+
Ok(())
298297
}
299298
}
300299

@@ -320,13 +319,8 @@ impl NotifyOnFileChange {
320319
impact_description,
321320
}
322321
}
323-
}
324322

325-
impl watchexec::handler::Handler<watchexec::action::Action> for NotifyOnFileChange {
326-
fn handle(
327-
&mut self,
328-
action: watchexec::action::Action,
329-
) -> std::result::Result<(), Box<dyn std::error::Error>> {
323+
fn handle(&self, action: watchexec::action::ActionHandler) -> watchexec::action::ActionHandler {
330324
if self.despurifier.all_spurious(&action) {
331325
tracing::debug!("spin watch ignored spurious changes: {}", paths_of(&action));
332326
} else {
@@ -337,12 +331,12 @@ impl watchexec::handler::Handler<watchexec::action::Action> for NotifyOnFileChan
337331
);
338332
_ = self.notifier.send(Uuid::new_v4());
339333
}
340-
action.outcome(watchexec::action::Outcome::DoNothing);
341-
Ok::<(), Box<dyn std::error::Error + 'static>>(())
334+
335+
action
342336
}
343337
}
344338

345-
fn paths_of(action: &watchexec::action::Action) -> String {
339+
fn paths_of(action: &watchexec::action::ActionHandler) -> String {
346340
action
347341
.events
348342
.iter()
@@ -353,13 +347,13 @@ fn paths_of(action: &watchexec::action::Action) -> String {
353347
.join(", ")
354348
}
355349

356-
fn path_of_event(event: &watchexec::event::Event) -> Option<&Path> {
350+
fn path_of_event(event: &watchexec_events::Event) -> Option<&Path> {
357351
event.tags.iter().filter_map(path_of_tag).next()
358352
}
359353

360-
fn path_of_tag(tag: &watchexec::event::Tag) -> Option<&Path> {
354+
fn path_of_tag(tag: &watchexec_events::Tag) -> Option<&Path> {
361355
match tag {
362-
watchexec::event::Tag::Path { path, .. } => Some(path),
356+
watchexec_events::Tag::Path { path, .. } => Some(path),
363357
_ => None,
364358
}
365359
}
@@ -369,7 +363,7 @@ mod despurifier {
369363
use std::collections::HashMap;
370364
use std::sync::Mutex;
371365
use std::time::SystemTime;
372-
use watchexec::event::{filekind::FileEventKind, Tag};
366+
use watchexec_events::{filekind::FileEventKind, Tag};
373367

374368
pub struct Despurifier {
375369
process_start_time: SystemTime,
@@ -384,13 +378,13 @@ mod despurifier {
384378
}
385379
}
386380

387-
pub fn all_spurious(&mut self, action: &watchexec::action::Action) -> bool {
381+
pub fn all_spurious(&self, action: &watchexec::action::ActionHandler) -> bool {
388382
action.events.iter().all(|e| self.all_spurious_evt(e))
389383
}
390384

391385
// This is necessary to check due to a bug on macOS emitting modify events on copies
392386
// https://github.com/rust-lang/rust/issues/107130
393-
fn all_spurious_evt(&mut self, event: &watchexec::event::Event) -> bool {
387+
fn all_spurious_evt(&self, event: &watchexec_events::Event) -> bool {
394388
// Deletions are never spurious
395389
if event
396390
.tags
@@ -438,7 +432,7 @@ mod despurifier {
438432
Self
439433
}
440434

441-
pub fn all_spurious(&mut self, _action: &watchexec::action::Action) -> bool {
435+
pub fn all_spurious(&self, _action: &watchexec::action::ActionHandler) -> bool {
442436
false
443437
}
444438
}
@@ -459,8 +453,8 @@ impl ReconfigurableWatcher {
459453
async fn start(
460454
rtf: RuntimeConfigFactory,
461455
) -> anyhow::Result<(Self, tokio::task::JoinHandle<()>)> {
462-
let rt = rtf.build_config().await?;
463-
let watcher = Watchexec::new(watchexec::config::InitConfig::default(), rt)?;
456+
let watcher = Arc::new(Watchexec::default());
457+
rtf.build_config(&watcher.config).await?;
464458
let watcher_clone = watcher.clone();
465459
let join_handle = tokio::task::spawn(async move {
466460
_ = watcher_clone.main().await;
@@ -477,16 +471,9 @@ impl ReconfigurableWatcher {
477471
pub async fn reconfigure(&self) {
478472
match self {
479473
Self::Actual((watchexec, rtf)) => {
480-
let rt = match rtf.build_config().await {
481-
Ok(rt) => rt,
482-
Err(e) => {
483-
tracing::error!("Unable to re-configure watcher after manifest change. Changes in files newly added to the application may not be detected. Error: {e}");
484-
return;
485-
}
486-
};
487-
if let Err(e) = watchexec.reconfigure(rt) {
474+
if let Err(e) = rtf.build_config(&watchexec.config).await {
488475
tracing::error!("Unable to re-configure watcher after manifest change. Changes in files newly added to the application may not be detected. Error: {e}");
489-
}
476+
};
490477
}
491478
Self::Dummy => (),
492479
}

src/commands/watch/buildifier.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use command_group::AsyncCommandGroup;
1+
use command_group::tokio::AsyncCommandGroup;
22
use std::path::PathBuf;
33
use uuid::Uuid;
44

@@ -59,7 +59,7 @@ impl Buildifier {
5959
}
6060
_ = self.watched_changes.changed() => {
6161
tracing::debug!("Cancelling build as there are new changes to process");
62-
child.kill()?;
62+
child.kill().await?;
6363
if self.clear_screen {
6464
_ = clearscreen::clear();
6565
}

0 commit comments

Comments
 (0)