-
Notifications
You must be signed in to change notification settings - Fork 1k
Conversation
This is implementation of the 2nd approach mentioned in #496 . Looking at code might help in better understanding :) Integration tests are failing due to change in behavior (Network mode by default). Will fix them once we have a properly discussed fix. |
Also, should dep tell the user about the execution mode, or how it's solving the dependency constraints?
|
As noted over there, I think this is the right approach to start with 😄
👍
Definitely. I'd go a step further, actually - I'll update the spec doc with some things to this effect. |
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're headed in a good direction 😄
cmd/dep/init.go
Outdated
P: make([]gps.LockedProject, 0, len(pd.ondisk)), | ||
} | ||
if cmd.gopath { | ||
pd, err = getProjectData(ctx, pkgT, cpr, sm) |
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.
The implementation of getProjectData()
is likely to need a fair bit of modification, as it should really only be filling in holes in the graph. I suspect that's going to result in nontrivial changes to that graph traversal algorithm.
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 thought I left a reply here 🤔 maybe my machine got disconnected and it wasn't saved.
So here again, would be better to create a dedicated issue to discuss how we modify getProjectData()
😬. Would be great if you could add some more details about the ideas you have in making it better. The function is huge and a lot of stuff seems to be happening in it. 😅
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.
For sure, it's a rabbit hole. Separate issue makes sense.
cmd/dep/init.go
Outdated
) | ||
for pr, v := range pd.ondisk { | ||
// That we have to chop off these path prefixes is a symptom of | ||
// a problem in gps itself |
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 comment might actually be out of date, I think I changed subpackage naming a while back. It wouldn't be a terrible thing if you checked on it while you were poking around in 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.
yes, I came across this while working on the previous init PR. I observed that the Lock obtained after a solve contains LockedProject
with proper list of packages required from the project, no chopping required. But I couldn't figure out how to create LockedProject's package list using gps, to avoiding the above chopping code block.
Can you create a new issue with some more details?
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.
But I couldn't figure out how to create LockedProject's package list using gps, to avoiding the above chopping code block.
Yeah there's a family of functions I've been considering adding for this, but right now everything's waiting behind getting gps moved into dep.
Can you create a new issue with some more details?
Sure, but...which part? Just the whole thing?
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.
No, for now just that chopping code block part. With little details about how to create LockedProject
with proper list of packages using gps, to replace the outdated comment and code.
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.
With little details about how to create
LockedProject
with proper list of packages using gps
Well, this is kinda what I'm getting at - the "properness" of the package list in LockedProject
's isn't separable from a full Solve()
. NewLockedProject()
will let you create instances as needed, but being proper/correct is only defined in the context of an overall dependency set, computed by solving.
So, is the question here about simply creating instances, or creating correct ones?
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.
Yeah, from what I have seen, a full solve is required to get a proper package list. I was wondering if there's a way to partially run solve (or some gps function) and get the same package list that we need to create locks.
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.
Nope, unfortunately, the problem can't be partitioned (in fact, it's NP-complete). It's all-or-nothing; hence why gps.Solution
is a superset of gps.Lock
.
d3c8160
to
c4e11d0
Compare
Will implement itemized feedback in another PR. Would create another issue for the same. |
Also, should we explicitly print the default network mode, "Using network for projects...", kinda message? |
👍 👍 👍 |
cmd/dep/init.go
Outdated
P: make([]gps.LockedProject, 0, len(pd.ondisk)), | ||
} | ||
if cmd.gopath { | ||
fmt.Fprintln(os.Stderr, "Searching GOPATH for projects...") |
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.
Let's use internal.Logf()
here
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.
oh! didn't see that existed. Found fmt.Fprintln()
being used in main.go
. So used the same 😅
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.
oh...yeah, hah, i think that's older stuff there. it should be updated 😄
c4e11d0
to
347af65
Compare
@sdboyer are we waiting for some changes here? 😅 Just making sure no change requests are pending. You can take your time :) |
@darkowlzz sorry, I meant to ask you the same 😄 We do need the general CLI helptext to be updated. We're in a transitional state with this, so it needn't be flawless, but we can't mislead users about behavior. |
15e4e54
to
588f47e
Compare
@sdboyer oops! forgot about that 😅 Updated! |
OK, needs just a little catchup after I merged #512 - then I can pull it into the feature branch that I mentioned before 😄 |
@sdboyer yep, already working on it. |
3b22acb
to
396dc2a
Compare
@sdboyer squashed, rebased and adjusted to work with current code. |
awesome. it looks like i should be able to get the feature branch up this afternoon. thanks! |
oh, we're totally ready for this now! this is in @carolynvs's domain, so marking her for review 😄 |
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.
Sorry, so much of the logic that you are building on has moved that a rebase is needed before I can review further.
cmd/dep/init.go
Outdated
|
||
- Tags conforming to semver (sorted by semver rules) | ||
- Default branch(es) (sorted lexicographically) | ||
- Non-semver tags (sorted lexicographically) | ||
|
||
An alternate mode can be activated by passing -gopath. In this mode, version of |
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.
In this mode, the version of...
cmd/dep/init.go
Outdated
l := &dep.Lock{ | ||
P: make([]gps.LockedProject, 0, len(pd.ondisk)), | ||
} | ||
if cmd.gopath { |
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 all this logic moved out of init.go into gopath_scanner.go, would you please rebase and move over your changes too? Sorry for the merge trouble!
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.
Here's where the various bits are in gopath_scanner:
- Scan to generate project data -> gopathScanner.scanGopathForDependencies
- Populate a manifest using the project data -> gopathScanner.InitializeRootManifestAndLock
- Combine the results of the importer with what we found in GOPATH, printing feedback for what was used (things from the importers win) -> gopathScanner.overlay
- Tweak the lock based on the results of solving -> gopathScanner.FinalizeRootManifestAndLock
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.
thanks :)
537b95b
to
8a82838
Compare
Rebased and made changes to work with new code. I feel the need to have some kind of loading animation or as simple as incremental dots ( |
|
||
[[constraint]] | ||
name = "github.com/sdboyer/deptest" | ||
version = "1.0.0" |
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.
Previously, this used to be with a ^
. Now, I think because of ImpliedCaretString
, versions are written as exact. Is it intended?
Also, the feedback that we print for network mode, prints with ^
right now. So there's a mismatch between what the feedback shows version and what's written in manifest.
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.
Yup, when init generates the manifest it should not write out the ^
. When the manifest is read, the caret is automatically added. Here's some backstory on why we don't include the caret in the manifest.
My preference is for the verbose output to print what's really going on and show the caret, but will leave that call to @sdboyer. If we do end up changing that, let's do it in a separate PR.
cmd/dep/init.go
Outdated
if cmd.gopath { | ||
gs.FinalizeRootManifestAndLock(m, l) | ||
} else { | ||
// Pick the direct dependencies from the above solution and add to manifest |
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.
Can you help me understand why you added this else clause? It looks like the old code from the gopath scanning but in this case is being applied to the results of importing config from an external tool.
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.
IIRC, this was done to generate the final lock memo with the obtained solution.
When there's no external tool and -gopath is not passed, we use an empty manifest in the solver to get a solution. Then we use the lock obtained from the solution and pick direct dependencies from there and create a new manifest, using which we generate the final lock memo.
Now, in case of an external tool, our initial manifest may or may not be empty. So we use the solution lock to fill up the manifest with direct dependencies. In case, manifest was empty, it gets filled. And in case, manifest already had packages, there wouldn't be any change. Or if there were any missing direct dependencies in manifest, they get added too.
Hope this answers your question.
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.
@carolynvs I just figured out why you asked this question. Went through the epic #186 and came across
ignore all the existing local information - GOPATH, existing tools, everything - and just have the solver pick out the latest versions it can.
😅 which is totally different from what's being done here. Lemme change that.
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.
To be honest I'm not entirely sure what's the right approach. 😊 I was just asking because I was confused.
What I am pretty sure about is:
- The logic should be in
rootAnalyzer.FinalizeManifestAndLock
instead of the init command directly. - When someone runs
dep init -skip-tools -gopath
it shouldn't result in a populated lock and an empty manifest.
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.
The logic should be in rootAnalyzer.FinalizeManifestAndLock instead of the init command directly.
Yeah, I felt I made init dirty. It became elegant with all the new stuff and with all these changes, it felt dirty 😅 I'll change that.
When someone runs dep init -skip-tools -gopath it shouldn't result in a populated lock and an empty manifest.
We have Integration/init/case3 to handle this scenarios 😊
3fdd31c
to
b858322
Compare
Made Much cleaner now :) |
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.
Apologies if my comments sent you off on the wrong path... 😊
This is super close, and should be ready to merge after shuffling things back a bit to the way they were initially.
cmd/dep/root_analyzer.go
Outdated
ctx: ctx, | ||
sm: sm, | ||
directDeps: directDeps, | ||
} | ||
} | ||
|
||
func (a *rootAnalyzer) InitializeRootManifestAndLock(dir string, pr gps.ProjectRoot) (rootM *dep.Manifest, rootL *dep.Lock, err error) { | ||
if !a.skipTools { | ||
// Use importer tools only in gopath mode | ||
if a.gopath && !a.skipTools { |
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 doesn't match the behavior outlined in the updated command spec
If -gopath is passed, then when the legacy tool import is complete (or skipped, in the event that -skip-tools is also passed), an additional search pass should be made through the current (as inferred from the cwd) GOPATH
As I read it, -gopath
and -skip-tools
are completely independent.
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 added a comment discussing this in the google doc: dep: updated command spec.
cmd/dep/root_analyzer.go
Outdated
if pr == y.Ident().ProjectRoot { | ||
used = true | ||
break | ||
if a.gopath { |
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 both paths of this if/else are safe to run unconditionally. If a direct dep was added during solve, either because the imported config was incomplete or due to "network solving", then it's appropriate to add it to the manifest. Same for removing unused dependencies from the manifest.
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 we stop using the a.gopath
in both functions, we can remove it entirely from the struct.
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.
We need some way to make root_analyzer aware of -gopath
flag. If else part of this is kept here itself, in root_analyzer, it would execute the feedback code. So, in gopath mode, feedbacks would be logged twice.
cmd/dep/gopath_scanner.go
Outdated
|
||
pd projectData | ||
origM *dep.Manifest | ||
origL *dep.Lock | ||
} | ||
|
||
func newGopathScanner(ctx *dep.Ctx, directDeps map[string]bool, sm gps.SourceManager) *gopathScanner { | ||
func newGopathScanner(ctx *dep.Ctx, directDeps map[string]bool, sm gps.SourceManager, gopath bool) *gopathScanner { |
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 this went a bit too far in trying to keep init "clean". 😀 Let's have the init command handle the -gopath
flag and call gopathScanner
or not based on its value.
b858322
to
8915341
Compare
cmd/dep/root_analyzer.go
Outdated
f = fb.NewLockedProjectFeedback(y, fb.DepTypeTransitive) | ||
} | ||
f.LogFeedback(a.ctx.Err) | ||
} |
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.
Problem with this is that, since rootAnalyzer
isn't aware of network/gopath mode of execution, even in gopath mode, these feedbacks would run. Resulting in double feedbacks.
Should this be moved to init? or should we make at least rootAnalyzer aware of gopath flag? or maybe there's another way 🤔
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.
@darkowlzz I wrote out the workflow for init to help answer this question and decided to dump my notes, instead of just the one part that addresses the question. 😁
rootAnalyzer.importManifestAndLock
- Import constraints and locked projects from external config.
gopathScanner.InitializeManifestAndLock
- Add constraints and locked projects from GOPATH.
- SOLVE
- Uses dark magic to add more goodies to the lock.
rootAnalyzer.FinalizeManifestAndLock
- Remove unused constraints. I'll remove this in a separate PR. I just realized that it's not needed anymore thanks to
rootAnalyzer.removeTransitiveDependencies
. - This PR adds printing feedback for direct deps found during solve.
- This PR adds printing feedback for transitive deps locked during solve.
- Remove unused constraints. I'll remove this in a separate PR. I just realized that it's not needed anymore thanks to
gopathScanner.FinalizeManifestAndLock
- Add constraints found during solve that weren't initially in the manifest
because they weren't on disk.
- Add constraints found during solve that weren't initially in the manifest
I believe that we can generalize the FinalizeManifestAndLock
activities to "Identify new constraints and locked projects found during solve". New constraints are direct deps that are in the final lock but not the manifest, and new locked projects are identified by keeping track of the initial lock. The same finalization tasks can be performed of how we got to this state:
-gopath
wasn't specified and nothing was imported, 100% network solve.- The GOPATH didn't have repos for all of the direct deps.
- The imported config was incomplete and didn't have constraints for all of the direct deps.
Then we could have init keep a copy of the lock given to the solver, and pass it in as an additional argument to rootAnalyzer.FinalizeManifestAndLock
. Now all that logic is in one place and we can also remove gopathScanner.FinalizeManifestAndLock
.
What do you 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.
I remember we used to keep a copy of lock given to the solver and use the same to identify new constraints and projects, by comparing with new lock, at one point of time. I still have my dep fork at that point of time, copyLock 😊
So, yeah, a unified FinalizeManifestAndLock
is what we need 🙌
d56d41b
to
0821c76
Compare
cmd/dep/init.go
Outdated
@@ -164,8 +175,8 @@ func (cmd *initCommand) Run(ctx *dep.Ctx, args []string) error { | |||
} | |||
p.Lock = dep.LockFromSolution(soln) | |||
|
|||
rootAnalyzer.FinalizeRootManifestAndLock(p.Manifest, p.Lock) | |||
gs.FinalizeRootManifestAndLock(p.Manifest, p.Lock) | |||
rootAnalyzer.RemoveTransitiveDependencies(p.Manifest) |
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.
@carolynvs I used RemoveTransitiveDependencies()
in this PR itself. Hopefully this is how you intended to use it 😅
0821c76
to
6ace212
Compare
- Removes gopathScanner's FinalizeManifestAndLock() and its usage. - Adds using removeTransitiveDependencies() to remove unused constraints. - Changes rootAnalyzer's FinalizeManifestAndLock to log feedback for new constraints and locked projects only.
6ace212
to
3eb07c3
Compare
cmd/dep/root_analyzer.go
Outdated
used = true | ||
break | ||
func (a *rootAnalyzer) FinalizeRootManifestAndLock(m *dep.Manifest, l *dep.Lock, ol dep.Lock) { | ||
a.removeTransitiveDependencies(m) |
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.
@carolynvs I used removeTransitiveDependencies()
in this PR itself. Hopefully this is how you intended to use it 😅
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.
AFAIK only the importers may add a transitive or unused dep to the manifest, because they are just responsible for converting from the external config to dep's. In rootAnalyzer.importManifestAndLock
we are calling removeTransitiveDependencies
after the importer is run, to clean up after them.
So unless someplace in the code else may be adding things to the manifest that may not belong, I don't think we need to call it again in FinalizeManifestAndLock
.
cmd/dep/root_analyzer.go
Outdated
pc := gps.ProjectConstraint{Ident: y.Ident(), Constraint: pp.Constraint} | ||
f = fb.NewConstraintFeedback(pc, fb.DepTypeDirect) | ||
} else { | ||
f = fb.NewLockedProjectFeedback(y, fb.DepTypeDirect) |
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.
When I split up the old feedback
function into the constructors, a single call no longer does "double duty". So NewConstraintFeedback
will only generate a using entry and NewLockedProjectFeedback
will only generate a locking entry.
What we have right here will only print either a using or a locking, but never both. Was that the intent? I expected that if we can deduce a constraint, that we could want to print the constraint and the locked project. If you agree, I think we need to add a LogFeedback
call after NewConstraintFeedback
and then unconditionally call NewLockedProjectFeedback
and LogFeedback
instead of putting it in this else clause.
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.
oh! yeah, that's right. Didn't realize that got changed 😅 no need of the conditional else.
- Removes `removeTransitiveDependencies()` from `FinalizeRootManifestAndLock`. - Moves locked project feedback for new direct deps out of conditional clause.
I opened #763 for one of the failed tests, I've seen it randomly fail a few times. Looks good after restarting the build! |
Thank you for staying on top of this and your patience with the uncertain expected behaviors! 💖 |
GOPATH. Solve the dependency constraints based on the projects found on
disk and use network to solve for projects not found on disk.
dependency constraints completely over network.
Fixes #496