From 8f215042dbf37c2d6f69fb566d5d44c347d3470b Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sun, 6 Oct 2024 12:29:03 -0600 Subject: [PATCH 1/4] client/push: Ignore empty lines --- client/src/command/push.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/src/command/push.rs b/client/src/command/push.rs index c93d22d..822d681 100644 --- a/client/src/command/push.rs +++ b/client/src/command/push.rs @@ -108,6 +108,10 @@ impl PushContext { let stdin = BufReader::new(io::stdin()); let mut lines = stdin.lines(); while let Some(line) = lines.next_line().await? { + if line.is_empty() { + continue; + } + let path = self.store.follow_store_path(line)?; session.queue_many(vec![path])?; } From 014fb92a9ee762784e4dc3b5b58eb38174928d03 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sun, 6 Oct 2024 12:29:03 -0600 Subject: [PATCH 2/4] client/push: Suggest --stdin if stdin isn't a terminal --- client/src/command/push.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/src/command/push.rs b/client/src/command/push.rs index 822d681..fdbc7a1 100644 --- a/client/src/command/push.rs +++ b/client/src/command/push.rs @@ -1,3 +1,4 @@ +use std::io::IsTerminal; use std::path::PathBuf; use std::sync::Arc; @@ -57,6 +58,16 @@ struct PushContext { impl PushContext { async fn push_static(self, paths: Vec) -> Result<()> { + if paths.is_empty() { + eprintln!("🤷 Nothing specified."); + if !std::io::stdin().is_terminal() { + eprintln!( + "Hint: Pass --stdin to read the list of store paths from standard input." + ); + } + return Ok(()); + } + let roots = paths .into_iter() .map(|p| self.store.follow_store_path(p)) From c54c26d82e5054eaa62ce65bc8f7330b48ded485 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sun, 6 Oct 2024 12:29:03 -0600 Subject: [PATCH 3/4] client/push: Disallow paths on the command line with --stdin --- client/src/command/push.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/src/command/push.rs b/client/src/command/push.rs index fdbc7a1..b2bb661 100644 --- a/client/src/command/push.rs +++ b/client/src/command/push.rs @@ -182,6 +182,12 @@ pub async fn run(opts: Opts) -> Result<()> { }; if sub.stdin { + if !sub.paths.is_empty() { + return Err(anyhow!( + "No paths can be specified on the command line with --stdin" + )); + } + push_ctx.push_stdin().await?; } else { push_ctx.push_static(sub.paths.clone()).await?; From 96383ccab4748d968e4549a1f5903564d464a806 Mon Sep 17 00:00:00 2001 From: Zhaofeng Li Date: Sun, 6 Oct 2024 12:29:03 -0600 Subject: [PATCH 4/4] integration-tests/basic: Add --stdin test --- integration-tests/basic/default.nix | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/integration-tests/basic/default.nix b/integration-tests/basic/default.nix index 9896d29..f54b467 100644 --- a/integration-tests/basic/default.nix +++ b/integration-tests/basic/default.nix @@ -203,12 +203,12 @@ in { with subtest("Check that we can push a path"): client.succeed("${makeTestDerivation} test.nix") - test_file = client.succeed("nix-build --no-out-link test.nix") + test_file = client.succeed("nix-build --no-out-link test.nix").strip() test_file_hash = test_file.removeprefix("/nix/store/")[:32] client.succeed(f"attic push test {test_file}") client.succeed(f"nix-store --delete {test_file}") - client.fail(f"grep hello {test_file}") + client.fail(f"ls {test_file}") with subtest("Check that we can pull a path"): client.succeed("attic use readonly:test") @@ -219,6 +219,25 @@ in { client.fail(f"attic push readonly:test {test_file}") client.fail(f"attic push anon:test {test_file} 2>&1") + with subtest("Check that we can push a list of paths from stdin"): + paths = [] + for i in range(10): + client.succeed(f"${makeTestDerivation} seq{i}.nix") + path = client.succeed(f"nix-build --no-out-link seq{i}.nix").strip() + client.succeed(f"echo {path} >>paths.txt") + paths.append(path) + + client.succeed("attic push test --stdin &1") + + for path in paths: + client.succeed(f"nix-store --delete {path}") + + with subtest("Check that we can pull the paths back"): + for path in paths: + client.fail(f"ls {path}") + client.succeed(f"nix-store -r {path}") + client.succeed(f"grep hello {path}") + with subtest("Check that we can make the cache public"): client.fail("curl -sL --fail-with-body http://server:8080/test/nix-cache-info") client.fail(f"curl -sL --fail-with-body http://server:8080/test/{test_file_hash}.narinfo")