-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
A different take on git submodule support for flakes #5497
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool. I had something like this in mind as well, but then decided for the simpler approach in #5399 for now.
{ | ||
auto rootTree = getRootTree(path, gitrev); | ||
printTalkative("root tree is %s", rootTree); | ||
auto entries = gitTreeList(path, rootTree); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this double git cat-file
different from git ls-tree $rev
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Idk
/cc @cleverca22
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like you can just skip directly to ls-tree
on a rev
but you still need to recurse manually, git ls-tree $rev $path/
for each dir
return results; | ||
} | ||
|
||
std::map<string,string> parseSubmodules(const string & contents) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since .gitmodules
is technically an arbitrarily formatted git config file, it might be nicer to have git parse the file into a stricter format, which can also be combined with locating & reading the blob: git config --blob $rev:.gitmodules --list
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a very neat trick thank you!
return results; | ||
} | ||
|
||
string findCommitHash(const Path & path, const std::map<string, std::pair<gitType, string>> & _entries, const string & pathToFind) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think git ls-tree $rev $submodulePath
gives you the same information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh, and that solves the recursion i mentioned a few comments up
src/libfetchers/git.cc
Outdated
prefix = prefix + "file://"; | ||
|
||
|
||
attrs.emplace(subPath, prefix + url + "?allRefs=1&rev=" + commitHash); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the submodule commit is available only in the local clone and not in the remote, we need to pass the former + submodule path as url
here I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually we should probably always do this if the commit is locally available to avoid redundant fetches
Would this be able to handle nested submodules? |
Not as conveniently as it would be with simply enabling submodules for The happy use case here is when each of your submodules is a flake itself, handling its own submodules without you having to care about it. With other use cases, such as requiring the recursively cloned source tree for the build itself, you'd have to write some nix code to manipulate the URL provided into an attrset (until #5434 adds |
fi | ||
|
||
clearStore | ||
rm -rf $TEST_HOME/.{cache,config} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's clearCache
... Why do you need to nuke config
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part was just copied verbatim from flakes.sh
not necessarily necessary but it helps me wrap my head around things
oddly this doesn't reproduce the problem I'm experiencing IRL where "dirtying" the submodule has no effect until the cache is either nuked or runs out of TTL
This allows changes to dirty submodules to propagate correctly and trigger a re-eval as one would expect. This previously only worked with `__contentAddressed = true;`.
@@ -257,6 +257,13 @@ std::optional<time_t> Input::getLastModified() const | |||
return {}; | |||
} | |||
|
|||
std::optional<std::string> Input::getModules() const | |||
{ | |||
if (auto s = maybeGetStrAttr(attrs, "modules")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm surprised that this works... Isn't modules an attrset? Doesn't maybeGetStrAttr
throw if the attribute isn't a string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's already stored as JSON. fetchTree
reconstructs it into an attrset here: https://github.com/NixOS/nix/pull/5497/files#diff-e473b01a82cc31e51baa89b26b4fe5083e56df27cd834b9722789841ef9d5620R38-R50
not the prettiest thing in the world but it works 🤷♀️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😱
Does the process producing the JSON sort keys?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, you're right. we only sort in fetchTree
😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... looks like nlohmann takes care of it, unless I'm missing something: https://json.nlohmann.me/features/object_order/
I rebased this on the latest master and pushed it to my fork under FlorianFranzen/nix/submodules. I have started using this instead of loading submodules by default and it safes me a lot of rebuilds. Would love to have |
We actually did try to implement that using a thunk at some point but it ended up causing an infinite recursion (IIRC) so was reverted and scoped out... December kinda forced a context switch and I haven't had the time to re-wrap my head around this but I really want to see this through as it's been a recurring pain point in my workflow. Anyways I'm happy it's useful. Help me draw more attention 😎 |
@cleverca22 Any chance we could get some input on this? I see you did some earlier reviews. I will have a look at how to implement a lazy version of this. If necessary I could also add an experimental feature flag for it. I would also like to see this integrated with |
@@ -37,6 +37,8 @@ path0_=$(nix eval --impure --raw --expr "(builtins.fetchTree { type = \"git\"; u | |||
export _NIX_FORCE_HTTP=1 | |||
[[ $(tail -n 1 $path0/hello) = "hello" ]] | |||
|
|||
ls -l $repo | |||
nix eval --debug --impure --raw --expr "(builtins.fetchGit file://$repo).outPath" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these really needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like debugging leftovers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really excited for this change. Please let us know if any additional support is needed- but this looks fine to me
@@ -37,6 +37,8 @@ path0_=$(nix eval --impure --raw --expr "(builtins.fetchTree { type = \"git\"; u | |||
export _NIX_FORCE_HTTP=1 | |||
[[ $(tail -n 1 $path0/hello) = "hello" ]] | |||
|
|||
ls -l $repo | |||
nix eval --debug --impure --raw --expr "(builtins.fetchGit file://$repo).outPath" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like debugging leftovers
commit | ||
}; | ||
|
||
string getBlob(const Path & path, const string & gitrev, const string & blobhash) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgive my ignorance, but is there a reason this isn't static
(given that getRootTree
is)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably because I suck at C++. @cleverca22 do you have a better answer? 😅
git -C $flakeWithSubmodules submodule add $nonFlakeDir | ||
git -C $flakeWithSubmodules submodule add $flakeDir | ||
git -C $flakeWithSubmodules add . | ||
git -C $flakeWithSubmodules commit -m'add submodules' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I were being a Mighty Stickler I'd request a test that declares this feature's behaviour in the presence of recursive submodules. (That is, repo A has a submodule B, which itself has a submodule C.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Ah, I see you commented above that this isn't really supported. And anyway I wouldn't consider this blocking!)
} | ||
|
||
/* Check whether this repo has any commits. There are | ||
probably better ways to do this. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strictly speaking there's a hilarious but more correct answer:
git cat-file --batch-check='%(objecttype)' --batch-all-objects --unordered | grep commit
But I'm sure any sane repo is in fact going to have an entry in /refs/heads
.
Does anyone know any blockers for this going in? And/or who has The Power To Merge? |
Would love to see this implemented for secrets management. |
@thegergo02 A bit of a hack now for submodule secrets management is to reference a local "fake flake" and then override it with Fake flake example: https://github.com/dmadisetti/.dots/blob/template/nix/spoof/flake.nix (I have a bit of templating stuff around it) |
Oh yuck, the merge with master is pretty hairy :( |
I do not mind putting in the work to get this rebased onto master. But I will need some feedback if this is something that actually has a chance of being merged. And given the ongoing work on #6530, it probably also makes sense to wait till that is merged. |
Makes two of us 🤭 |
Triaged in the Nix Team meeting this morning (n.b. I could not make it):
|
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2023-08-04-nix-team-meeting-minutes-77/31487/1 |
Discussed in Nix team meeting 2023-10-13:
@FlorianFranzen @kaii-zen @Ericson2314 would one of you be interested in splitting out the tests? |
This pull request has been mentioned on NixOS Discourse. There might be relevant details there: https://discourse.nixos.org/t/2023-10-13-nix-team-meeting-minutes-94/34395/1 |
As the title implies, this is a whole 'nother take. Ça veut dire, it does not interact AT ALL with
submodules = true;
. It does involve more code though.Idea
If a tree has a
.gitmodules
in it, then we:rev
sgit+xxxx://host/who/what?rev=xxxx
etc)fetchTree
That's it. It looks like this:
It's just plain contextless strings so unless I'm fundamentally misunderstanding things, no dragons lie therein.
From here the user is empowered to do as much or as little as she wants with it: use
builtins.getFlake
for a flake,builtins.fetchTree
for a non-flake, or ignore it altogether.See
tests/flakes-with-submodules.sh
for a (rudimentary) example of how this might be used within a flake.This could potentially be used as a basis for a more "flakeful" UX, where submodules behave like
indirect
(registry) inputs and get populated with the Right Thing if and only if they get added to theoutputs
function args set... or not.Since my C++ is rusty and my familiarity with the Nix codebase is flakey (both puns intended btw 🙃), I invite y'all to punch holes in my reasoning and point out any blind spots so that we can address them and hopefully reach a solution that everyone's happy with.
I trust that this would save the sanity of at least a handful of Nixers/Nixresses out there who are forced to interact with git submodules against their better judgement (or in accordance with their poor judgement. to each their own 🤷♀️), thereby empowering them to once again pursue their (title-cased) Hopes and Dreams!
kk 'nuff prose. known issues/caveats:
rm -rf ~/.cache/nix
is required (otherwise you'd geterror: input attribute 'modules' is missing
)dirty submodule scenario isn't handled yetadded, and fixed so that it also works without__contentAddressed = true;
(thanks @shlevy!)self
to havesubmodules = true;
" use case (Support self-fetching parameters in flake.nix #5312)Shout out to @cleverca22 for essentially onboarding me to the codebase (... and writing most of the harder parts)
Cheers