Daniel Pardo
07/17/2024, 9:20 AMbuild.gradle file, the custom configuration works to resolve a single dependency, that is used by a custom task to unzip its content and move some webapp static files to a folder before compiling the final application.
After spending hours and hours doing multiple tries, I found the root issue, the dependency of the custom configuration is resolved if I configure the repository statically in project.repositories{} (from the plugin nor the build.gradle 's project file), but it fails (401 Auth error) if the repository is configured from project.afterEvaluate{}, and I need to configure those repositories from inside afterEvaluate cause there is a Gradle extension with dynamic data of those repositories configured at the `build.gradle`'s project level.
Any clue, help, or something that I could do? thank youVampire
07/17/2024, 1:37 PMI need to configure those repositories from insideThis part is most probably wrong, the "need to".afterEvaluate
afterEvaluate is almost always a bad idea.
The main earnings from using it are ordering problems, timing problems, and race conditions.
Using afterEvaluate instead of finding a proper alternative way to do something is like calling Platform.runLater or SwingUtilities.invokeLater to "fix" a GUI problem.
It usually just shifts the problem to a later more hard to find, more hard to debug, more hard to reproduce, and more hard to fix point in time.
There are rare cases where afterEvaluate is necessary nowadays, for example if you need to interact with some plugin that itself badly uses afterEvaluate with which you have to cooperate, or if you need to bridge Property-based things with legacy primitive-based things.
So the best fix indeed would be to change your plugin to remove the need to use afterEvaluate. You could for example in your extension instead of having some properties, have a function that get those values as arguments and then do the configuration based on those values from within the function in the extension. Or if you prefer the DSL-like configuration you could do it in a way that looks myExtension { whatever { propertyA = "a" } } where whatever is a function that takes an Action<MyPropertyHolder> and in the function you create an instance of MyPropertyHolder, give that to the supplied action so that the properties get set, and after that again right away do the configuration.
That you get this 401 error probably means, that your plugins configuration is not done yet and thus the repository answers with 401 which makes resolution fail. So this probably means, that this build we are talking about is resolving this custom configuration at configuration time, which in itself again is a bad idea and discouraged. There are even voices to forbid such a premature resolution: https://github.com/gradle/gradle/issues/2298
It is very unlikely that the build in question really needs to do the resolution at configuration time but just does it lightly, so this should most probably also be improved to only do the resolution at execution time. How to properly do that I cannot answer right away without seeing what that build actually does exactly there.Daniel Pardo
07/17/2024, 2:39 PM401 is due to Gradle trying to resolve the dependency from the custom configuration before/parallel the repository is being configured from inside afterEvaluate.
"It is very unlikely that the build in question really needs to do the resolution at configuration time but just does it lightly, so this should most probably also be improved to only do the resolution at execution time."Regarding this, before coming here, my research pointed to do something like this, nor force the custom config to re-evaluate the repositories later on... but I didn't find doc about it either. thank you
Vampire
07/17/2024, 3:10 PMafterEvaluate is necessary in that situation.Daniel Pardo
07/18/2024, 10:27 AMfrom.zipTree(configurations.customConfig.singleFile).matching ...by
from {
zipTree(configurations.customConfig.singleFile).matching ...
}ChatGPT guided me on understanding that apparently by calling directly the from object methods, the action performs at the configuration time, but by doing it through the closure it does it at the execution time. (It works and if it is right I would like to argue that it is so confusing) thank you again @Vampire
Vampire
07/18/2024, 11:47 AMdespite there are other points I don't understandFeel free to ask about them, here if relevant to the thread or maybe better in new threads if unrelated or only loosely related
I fixed them by changingYeah, that was the one part I mentioned. Still it would be better to also fix the other part, that
afterEvaluate is actually used. š
> from.zipTree(configurations.customConfig.singleFile).matching ...
by
> from {
> zipTree(configurations.customConfig.singleFile).matching ...
> }Cannot say much to that, as I have no idea what
from is or which zipTree function.
Because in Gradle built-in is zipTree only on ArchiveOperations, Project, and Script and to me it does not look like from should be any of those.
So the difference between from.zipTree and from { zipTree } seems to not come from Gradle itself.
Though it could well be (but does not always have to) that something you have in a lambda is evaluated at least deferred while without is executed directly.
There are other cases in the Gradle API where it indeed is like that.
ChatGPT guided me on understandingI'm sure you know it already, but never trust anything ChatGPT says. ChatGPT is utterly bad in giving correct answers. It is pretty good though in giving answers that look correct. It is nice to do work for you, you are too lazy to do yourself but could have done yourself (the good kind of lazy, not the bad one). But you always have to understand what it told you, and correct the non-sense it did. Asking about things you do not know yourself is pretty problematic, as it always gives an answer that looks very reasonable but often is wrong or at least incomplete.
Daniel Pardo
07/18/2024, 2:00 PMfrom comes from org.gradle.api.tasks.AbstractCopyTask#from(java.lang.Object...) (version 8.2)
⢠zipTree comes from org.gradle.api.Project#zipTree
About ChatGPT I agree, and indeed using it was a consequence..., I use it a lot with personal projects working with common and well-known tech stacks which I'm not very used to, simple things like "code a PieChart with MUI using the series from the entries x, y, z in the map", and it is really nice there avoiding hours of reading (at the cost of learning but you now, individual performance matters...).
With challenges like this one, where I don't know the context, I always try to go to the official documentation, as I stated in the first message, but not being able to find what I was looking for, partially because I didn't know about what I was looking for at the 100%, I found on it a suitable way to find something. It was useful sometimes, but indeed it was the "best" liar the most of the times and for this particular investigation I ended up here with much better result.
I think develop Gradle Plugins is not an easy thing because there are many understanding points hided which make it a real challenge, you realize it when things start to breaking apart, I feel that there are many documentation in internet, like SO threads, blogs and so on which promote bad practices (I learnt afterEvaluate in one of those), which is very scaring.... So I think the best bet to avoid most of the community following bad practices would be by having the best documentation as possible.Vampire
07/18/2024, 2:35 PMfor giving you more context about those changes.
⢠Thatcomes fromfrom(versionorg.gradle.api.tasks.AbstractCopyTask#from(java.lang.Object...))8.2
ā¢Then make sure in the future to copy code, not retype it. Because with those,comes fromzipTreeorg.gradle.api.Project#zipTree
from.zipTree(...) would not compile, but probably from(zipTree(...)) or from zipTree(...) as you are still on Groovy DSL š
A better lazy evaluation would be
from(configurations.customConfig.elements.map {
assert it.size() == 1
zipTree(it.first()).matching ...
})
there are many documentation in internet, like SO threads, blogs and so on which promote bad practicesYes, unfortunately there is many bad advice out there, or also things that might have been best practice or sole option some years ago but are highly discouraged now.