Skip to content

Commit 06cf001

Browse files
authored
fix: Audit 05/07 (#1955)
* Fix git submodules. * Fix clean hydration. * Update stdin. * Bump versions. * Add timeout. * Add config command. * Fix output prefixing.
1 parent 9e327f3 commit 06cf001

13 files changed

Lines changed: 111 additions & 19 deletions

File tree

.yarn/versions/03d1ee1e.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
releases:
2+
"@moonrepo/cli": patch
3+
"@moonrepo/core-linux-arm64-gnu": patch
4+
"@moonrepo/core-linux-arm64-musl": patch
5+
"@moonrepo/core-linux-x64-gnu": patch
6+
"@moonrepo/core-linux-x64-musl": patch
7+
"@moonrepo/core-macos-arm64": patch
8+
"@moonrepo/core-macos-x64": patch
9+
"@moonrepo/core-windows-x64-msvc": patch

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
#### 🚀 Updates
6+
7+
- Updated `moon run` to support passing touched files via stdin.
8+
- Updated commands that wait for stdin to continue after 10 seconds if nothing was received.
9+
10+
#### 🐞 Fixes
11+
12+
- Fixed an issue where Git v2 would error loading submodules that haven't been checked out yet.
13+
- Fixed an issue with remote cache hydration that would leave around stale artifacts.
14+
- Fixed an issue where parallel persistent tasks wouldn't prefix output.
15+
316
## 1.35.5
417

518
#### 🐞 Fixes
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use crate::session::MoonSession;
2+
use starbase::AppResult;
3+
4+
pub async fn debug_config(session: MoonSession) -> AppResult {
5+
dbg!(&session.workspace_config);
6+
7+
dbg!(&session.toolchain_config);
8+
9+
dbg!(&session.tasks_config);
10+
11+
Ok(None)
12+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
pub mod config;
12
pub mod vcs;
23

34
use clap::Subcommand;
45

56
#[derive(Clone, Debug, Subcommand)]
67
pub enum DebugCommands {
8+
#[command(name = "config", about = "Debug loaded configuration.")]
9+
Config,
10+
711
#[command(name = "vcs", about = "Debug the VCS.")]
812
Vcs,
913
}

crates/app/src/commands/query.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use starbase::AppResult;
1414
use starbase_styles::color;
1515
use starbase_utils::json;
1616
use std::collections::BTreeMap;
17-
use tracing::{instrument, warn};
17+
use tracing::{debug, instrument, warn};
1818

1919
const HEADING_AFFECTED: &str = "Affected by";
2020
const HEADING_FILTERS: &str = "Filters";
@@ -574,6 +574,8 @@ pub async fn touched_files(session: MoonSession, args: QueryTouchedFilesArgs) ->
574574
status: args.status,
575575
};
576576

577+
debug!("Querying for touched files");
578+
577579
let result = query_touched_files(&vcs, &options).await?;
578580

579581
// Write to stdout directly to avoid broken pipe panics

crates/app/src/commands/run.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::components::run_action_pipeline;
2-
use crate::queries::touched_files::{QueryTouchedFilesOptions, query_touched_files};
2+
use crate::queries::touched_files::{QueryTouchedFilesOptions, query_touched_files_with_stdin};
33
use crate::session::MoonSession;
44
use clap::Args;
55
use iocraft::prelude::element;
@@ -134,7 +134,7 @@ pub async fn run_target(
134134
// Always query for a touched files list as it'll be used by many actions
135135
let touched_files = if vcs.is_enabled() {
136136
let local = is_local(args);
137-
let result = query_touched_files(
137+
let result = query_touched_files_with_stdin(
138138
&vcs,
139139
&QueryTouchedFilesOptions {
140140
default_branch: !local && !is_test_env(),

crates/app/src/queries/touched_files.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ use rustc_hash::FxHashSet;
77
use serde::{Deserialize, Serialize};
88
use starbase_styles::color;
99
use starbase_utils::json;
10-
use std::io::{IsTerminal, Read, stdin};
10+
use std::io::{IsTerminal, stdin};
11+
use std::time::Duration;
12+
use tokio::io::AsyncReadExt;
13+
use tokio::time::timeout;
1114
use tracing::{debug, trace, warn};
1215

1316
#[derive(Clone, Default, Deserialize, Serialize)]
@@ -54,8 +57,6 @@ pub async fn query_touched_files(
5457
vcs: &BoxedVcs,
5558
options: &QueryTouchedFilesOptions,
5659
) -> miette::Result<QueryTouchedFilesResult> {
57-
debug!("Querying for touched files");
58-
5960
let bag = GlobalEnvBag::instance();
6061
let default_branch = vcs.get_default_branch().await?;
6162
let current_branch = vcs.get_local_branch().await?;
@@ -157,24 +158,37 @@ pub async fn query_touched_files_with_stdin(
157158
vcs: &BoxedVcs,
158159
options: &QueryTouchedFilesOptions,
159160
) -> miette::Result<QueryTouchedFilesResult> {
161+
debug!("Querying for touched files");
162+
160163
let mut buffer = String::new();
161164

162165
// Only read piped data when stdin is not a TTY,
163166
// otherwise the process will hang indefinitely waiting for EOF.
164167
if !stdin().is_terminal() {
165-
stdin().read_to_string(&mut buffer).into_diagnostic()?;
168+
if let Ok(read_result) = timeout(
169+
Duration::from_secs(10),
170+
tokio::io::stdin().read_to_string(&mut buffer),
171+
)
172+
.await
173+
{
174+
read_result.into_diagnostic()?;
175+
}
166176
}
167177

168178
// If piped via stdin, parse and use it
169179
if !buffer.is_empty() {
170180
// As JSON
171181
if buffer.starts_with('{') {
182+
debug!("Received from stdin as JSON");
183+
172184
let result: QueryTouchedFilesResult = json::parse(&buffer)?;
173185

174186
return Ok(result);
175187
}
176188
// As lines
177189
else {
190+
debug!("Received from stdin as separate lines");
191+
178192
let files =
179193
FxHashSet::from_iter(buffer.split('\n').map(WorkspaceRelativePathBuf::from));
180194

crates/cli/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ async fn main() -> MainResult {
149149
commands::completions::completions(session, args).await
150150
}
151151
Commands::Debug { command } => match command {
152+
DebugCommands::Config => commands::debug::config::debug_config(session).await,
152153
DebugCommands::Vcs => commands::debug::vcs::debug_vcs(session).await,
153154
},
154155
Commands::Docker { command } => match command {

crates/task-runner/src/command_executor.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,13 @@ impl<'task> CommandExecutor<'task> {
290290
}
291291

292292
fn prepare_state(&mut self, context: &ActionContext, report_item: &mut TaskReportItem) {
293-
let is_primary = context.is_primary_target(&self.task.target);
294293
let is_ci = is_ci_env();
294+
let is_primary = context.is_primary_target(&self.task.target);
295+
let is_only_primary = is_primary && context.primary_targets.len() == 1;
295296

296297
// When a task is configured as local (no caching), or the interactive flag is passed,
297298
// we don't "capture" stdout/stderr (which breaks stdin) and let it stream natively.
298-
if !self.task.options.cache && context.primary_targets.len() == 1 && !is_ci {
299+
if !self.task.options.cache && is_only_primary && !is_ci {
299300
self.interactive = true;
300301
}
301302

@@ -308,10 +309,7 @@ impl<'task> CommandExecutor<'task> {
308309
};
309310

310311
// If only a single persistent task is being ran, we should not prefix the output.
311-
if context.primary_targets.len() == 1
312-
&& is_primary
313-
&& (self.task.is_persistent() || self.task.deps.is_empty())
314-
{
312+
if is_only_primary && (self.task.is_persistent() || self.task.deps.is_empty()) {
315313
report_item.output_prefix = None;
316314
}
317315

crates/task-runner/src/output_hydrater.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,7 @@ impl OutputHydrater<'_> {
9999
color::muted_light(error.to_string()),
100100
);
101101

102-
// Delete target outputs to ensure a clean slate
103-
for output in &self.task.output_files {
104-
fs::remove_file(output.to_logical_path(&self.app.workspace_root))?;
105-
}
102+
self.delete_existing_outputs()?;
106103
}
107104

108105
Ok(true)
@@ -114,6 +111,8 @@ impl OutputHydrater<'_> {
114111
state: &mut ActionState<'_>,
115112
) -> miette::Result<bool> {
116113
if let Some(remote) = RemoteService::session() {
114+
self.delete_existing_outputs()?;
115+
117116
match remote.restore_action_result(state).await {
118117
Ok(restored) => {
119118
return Ok(restored);
@@ -132,4 +131,12 @@ impl OutputHydrater<'_> {
132131

133132
Ok(false)
134133
}
134+
135+
fn delete_existing_outputs(&self) -> miette::Result<()> {
136+
for output in self.task.get_output_files(&self.app.workspace_root, true)? {
137+
fs::remove_file(output)?;
138+
}
139+
140+
Ok(())
141+
}
135142
}

0 commit comments

Comments
 (0)