Skip to content

Commit d07c24f

Browse files
committed
nix flake clone: Support all input types
For input types that have no concept of cloning, we now default to copying the entire source tree.
1 parent 95da93c commit d07c24f

File tree

6 files changed

+34
-14
lines changed

6 files changed

+34
-14
lines changed

src/libfetchers/fetchers.cc

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "nix/fetchers/fetch-settings.hh"
77
#include "nix/fetchers/fetch-to-store.hh"
88
#include "nix/util/url.hh"
9+
#include "nix/util/archive.hh"
910

1011
#include <nlohmann/json.hpp>
1112

@@ -377,10 +378,10 @@ Input Input::applyOverrides(std::optional<std::string> ref, std::optional<Hash>
377378
return scheme->applyOverrides(*this, ref, rev);
378379
}
379380

380-
void Input::clone(const Settings & settings, const std::filesystem::path & destDir) const
381+
void Input::clone(const Settings & settings, ref<Store> store, const std::filesystem::path & destDir) const
381382
{
382383
assert(scheme);
383-
scheme->clone(settings, *this, destDir);
384+
scheme->clone(settings, store, *this, destDir);
384385
}
385386

386387
std::optional<std::filesystem::path> Input::getSourcePath() const
@@ -493,9 +494,19 @@ void InputScheme::putFile(
493494
throw Error("input '%s' does not support modifying file '%s'", input.to_string(), path);
494495
}
495496

496-
void InputScheme::clone(const Settings & settings, const Input & input, const std::filesystem::path & destDir) const
497+
void InputScheme::clone(
498+
const Settings & settings, ref<Store> store, const Input & input, const std::filesystem::path & destDir) const
497499
{
498-
throw Error("do not know how to clone input '%s'", input.to_string());
500+
if (std::filesystem::exists(destDir))
501+
throw Error("cannot clone into existing path %s", destDir);
502+
503+
auto [accessor, input2] = getAccessor(settings, store, input);
504+
505+
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s' to %s...", input2.to_string(), destDir));
506+
507+
auto source = sinkToSource([&](Sink & sink) { accessor->dumpPath(CanonPath::root, sink); });
508+
509+
restorePath(destDir, *source);
499510
}
500511

501512
std::optional<ExperimentalFeature> InputScheme::experimentalFeature() const

src/libfetchers/git.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ struct GitInputScheme : InputScheme
278278
return res;
279279
}
280280

281-
void clone(const Settings & settings, const Input & input, const std::filesystem::path & destDir) const override
281+
void clone(const Settings & settings, ref<Store> store, const Input & input, const std::filesystem::path & destDir)
282+
const override
282283
{
283284
auto repoInfo = getRepoInfo(input);
284285

src/libfetchers/github.cc

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -426,12 +426,13 @@ struct GitHubInputScheme : GitArchiveInputScheme
426426
return DownloadUrl{parseURL(url), headers};
427427
}
428428

429-
void clone(const Settings & settings, const Input & input, const std::filesystem::path & destDir) const override
429+
void clone(const Settings & settings, ref<Store> store, const Input & input, const std::filesystem::path & destDir)
430+
const override
430431
{
431432
auto host = getHost(input);
432433
Input::fromURL(settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input)))
433434
.applyOverrides(input.getRef(), input.getRev())
434-
.clone(settings, destDir);
435+
.clone(settings, store, destDir);
435436
}
436437
};
437438

@@ -507,15 +508,16 @@ struct GitLabInputScheme : GitArchiveInputScheme
507508
return DownloadUrl{parseURL(url), headers};
508509
}
509510

510-
void clone(const Settings & settings, const Input & input, const std::filesystem::path & destDir) const override
511+
void clone(const Settings & settings, ref<Store> store, const Input & input, const std::filesystem::path & destDir)
512+
const override
511513
{
512514
auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
513515
// FIXME: get username somewhere
514516
Input::fromURL(
515517
settings,
516518
fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
517519
.applyOverrides(input.getRef(), input.getRev())
518-
.clone(settings, destDir);
520+
.clone(settings, store, destDir);
519521
}
520522
};
521523

@@ -596,14 +598,15 @@ struct SourceHutInputScheme : GitArchiveInputScheme
596598
return DownloadUrl{parseURL(url), headers};
597599
}
598600

599-
void clone(const Settings & settings, const Input & input, const std::filesystem::path & destDir) const override
601+
void clone(const Settings & settings, ref<Store> store, const Input & input, const std::filesystem::path & destDir)
602+
const override
600603
{
601604
auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht");
602605
Input::fromURL(
603606
settings,
604607
fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
605608
.applyOverrides(input.getRef(), input.getRev())
606-
.clone(settings, destDir);
609+
.clone(settings, store, destDir);
607610
}
608611
};
609612

src/libfetchers/include/nix/fetchers/fetchers.hh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public:
143143

144144
Input applyOverrides(std::optional<std::string> ref, std::optional<Hash> rev) const;
145145

146-
void clone(const Settings & settings, const std::filesystem::path & destDir) const;
146+
void clone(const Settings & settings, ref<Store> store, const std::filesystem::path & destDir) const;
147147

148148
std::optional<std::filesystem::path> getSourcePath() const;
149149

@@ -216,7 +216,8 @@ struct InputScheme
216216

217217
virtual Input applyOverrides(const Input & input, std::optional<std::string> ref, std::optional<Hash> rev) const;
218218

219-
virtual void clone(const Settings & settings, const Input & input, const std::filesystem::path & destDir) const;
219+
virtual void clone(
220+
const Settings & settings, ref<Store> store, const Input & input, const std::filesystem::path & destDir) const;
220221

221222
virtual std::optional<std::filesystem::path> getSourcePath(const Input & input) const;
222223

src/nix/flake.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ struct CmdFlakeClone : FlakeCommand
10491049
if (destDir.empty())
10501050
throw Error("missing flag '--dest'");
10511051

1052-
getFlakeRef().resolve(fetchSettings, store).input.clone(fetchSettings, destDir);
1052+
getFlakeRef().resolve(fetchSettings, store).input.clone(fetchSettings, store, destDir);
10531053
}
10541054
};
10551055

tests/functional/flakes/flakes.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,10 @@ tar cfz "$TEST_ROOT"/flake.tar.gz -C "$TEST_ROOT" flake5
369369

370370
nix build -o "$TEST_ROOT"/result file://"$TEST_ROOT"/flake.tar.gz
371371

372+
nix flake clone "file://$TEST_ROOT/flake.tar.gz" --dest "$TEST_ROOT/unpacked"
373+
[[ -e $TEST_ROOT/unpacked/flake.nix ]]
374+
expectStderr 1 nix flake clone "file://$TEST_ROOT/flake.tar.gz" --dest "$TEST_ROOT/unpacked" | grep 'existing path'
375+
372376
# Building with a tarball URL containing a SRI hash should also work.
373377
url=$(nix flake metadata --json file://"$TEST_ROOT"/flake.tar.gz | jq -r .url)
374378
[[ $url =~ sha256- ]]

0 commit comments

Comments
 (0)