-
Notifications
You must be signed in to change notification settings - Fork 1k
Respect dry run flag during dep ensure #256
Conversation
1805a28
to
126753d
Compare
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 first looked at this, turning on
-v
by default when-n
is present, seemed like an easy win. However the verbose output from the gps solver wasn't really what I was looking for in a dry-run, I wanted to know what ensure would do, not what it evaluated.
Yeah, the trace output is a whole different beast.
So I added output which clearly shows what will be written to the filesystem when the command is run without
-n
.
I'm starting to think this maybe isn't the best approach. Chewing on this a bit, it's seeming to me like this PR should maybe do a few different things:
- Break up
SafeWriter.WriteAllSafe()
into a function that takes a variable argument list similar toSafeWriter
's fields right now, but instead returns some type of object where the decisions have all been made about which states are being written, and exposes that information - and THEN you call a method on that return value to do the writes. This would let you avoid having to reimplement the detection logic in the printing func. - Instead of indicating the entirety of the lock that would be written, I think it would be more valuable to simply indicate what would change. That means a fair bit more work, though - we'll need a sister function to
locksAreEquivalent()
, saydiffLocks()
, that shows a diff of thegps.LockedProject
s in two locks. (TBH, that might even be better to do as a func in gps, as it would be entirely generic). - Being that we're moving towards not programatically writing to the manifest anymore, I don't think there's any need to worry about a diff output for that - we can keep with the straight dump, and then it'll just fall into disuse.
cmd/dep/ensure.go
Outdated
return errors.Wrap(sw.WriteAllSafe(writeV), "grouped write of manifest, lock and vendor") | ||
} | ||
|
||
func printDryRunActions(sw dep.SafeWriter, writeV bool) error { | ||
if sw.Manifest != nil { |
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.
hmm...this check isn't great, because it's now effectively duplicating write-detection logic that exists in the SafeWriter
. They'd now need to be updated in both places at the same time, or the logic drifts out of whack.
I'm also not sure that the subsequent checks are even quite the same - e.g., we don't write the Lock even if it's passed if it's equal to an existing lock.
cmd/dep/ensure.go
Outdated
|
||
if sw.Lock != nil { | ||
fmt.Println("Would have written the following lock.json:") | ||
m, err := sw.Lock.MarshalJSON() |
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, like here, the lock we actually write out depends on comparison of the old and existing lock.
I realize that's a bunch of things - please feel free to ping me in slack to plan a bit if you think it'd be easier to make a plan sync rather than async. |
Just pushed up my WIP. I have what was in the initial PR refactored as we discussed. Next step I'll work on diffing the locks. |
I lied, worked on improving the tests first. 😄 Today I'll work on diffing the locks. |
I have a diff but haven't tackled what the console output should look like yet. I was thinking something like this?
|
@sdboyer Alrighty, this is ready for review again. I realize that this got bigger than we both originally thought. If you prefer that I tidy up the commits so that this can be split up into smaller PRs, just let me know.
|
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.
OK, I've done a first pass review on this. I didn't dig TOO deep into the revised mechanics of SafeWriter
(i'll try to do so over the weekend at some point), but it seems like we're on a good path. Thus far, I've mostly got small nits.
The test harness improvements look generally good for this purpose, but let's hold off on using the improved harness more widely. There's a bunch of testing structure changes pending in #265, and I'd like to minimize conflicts. (Once we get both of these merged, I think we can step back a bit and figure out the next step of really making this testing system work well)
txn_writer.go
Outdated
// TODO(carolynvs) this should be moved to gps | ||
type LockedProjectDiff struct { | ||
Name gps.ProjectRoot `json:"name"` | ||
Repository *StringDiff `json:"repo,omitempty"` |
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 stick with calling this Source
, as it is elsewhere. "repo" busts through some abstraction levels that, while we don't need them now, I suspect we'll want them later :)
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.
@sdboyer Do you want the json name changed as well? Right now it's repo
which matches what is output in the lock file.
txn_writer.go
Outdated
Repository *StringDiff `json:"repo,omitempty"` | ||
Version *StringDiff `json:"version,omitempty"` | ||
Branch *StringDiff `json:"branch,omitempty"` | ||
Revision *StringDiff `json:"revision,omitempty"` |
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.
curious why these are all pointers
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 the diff, I did not want to print the field if empty. For example, if only the revision is different, then that is the only field displayed in the diff. This was the only way I could figure out to get omitempty to work. When I used a struct, instead of a pointer, the other fields were output with an empty string value.
txn_writer.go
Outdated
return nil | ||
} | ||
|
||
// diffLocks compares two locks and idenitfies the differences between them. |
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.
nit: s/idenitfies/identifies/
|
||
// diffProjects compares two projects and identifies the differences between them. | ||
// Returns nil if there are no differences | ||
// TODO(carolynvs) this should be moved to gps and updated once the gps unexported fields are available to use. |
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've actually been meaning to change gps to export those fields. The thing I was trying to do (make it logically impossible for LockedProject
s to have nil Version
data) by not exporting them in the first place was dumb, awkward, and didn't even accomplish the goal.
Doesn't matter too much, though - b/c yes, we'll be promoting this up to gps, anyway. 😄
txn_writer.go
Outdated
type LockDiff struct { | ||
HashDiff *StringDiff | ||
Add []LockedProjectDiff | ||
Remove []gps.ProjectRoot |
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.
At this level, I think it might make more sense to just use []LockedProjectDiff
for Remove, as well. The implementation here only prints the ProjectRoot
, but if we're thinking generically, it seems worth it to me that we preserve both sides of the information. That way, we can experiment with the output later without having to tinker with the underlying implementation.
Oh and also 🎊 🎊 🎉 🎉 😄 🎊 holy crap what a lot of work here - thank you! |
Glad to know I didn't give you a 💩 to review over the weekend. I know it's hard to tell someone that their "mega PR" needs more work. I'll get this updated tomorrow. Thanks for the review! |
756aaf8
to
0d77dff
Compare
I had to rebase due to conflicting changes in the testdata files and while I was at it squashed my WIP commits. @sdboyer This is ready for review again. I've made the following changes:
|
This is totally a dumb oversight on my part. It needs to be renamed in the lock, and the struct. I'm going to try to do another review tonight. Thanks for keeping with it! |
@sdboyer I've renamed |
@carolynvs ahh, sorry, i should've been clear - while that renaming does need to happen, let's do it in a separate PR. it's going to potentially break some peoples' So, just pull out that last commit, and we're ready to merge. woohoo! |
@sdboyer Haha, I thought that seemed a bit aggressive. 😉 All fixed! After this is merged, I'll submit another PR for the rename. |
@sdboyer Since it sounds like gps is going to move soon into the dep repo, when would you like a PR moving the bits flagged in this PR to move into the gps package? |
@carolynvs i think it'd be fine to do that in gps now. i won't get around to moving gps until next week, i think, so we might as well use the time window we have :) small conflict to resolve now that we've merged #287 - take care of that, and we're good to go! (also, do you mind opening a follow-up issue for the s/repo/source/ change?) |
Allows tests to easily read lock/manifest files from any directory, not just the testdata directory
@sdboyer I've rebased and updated the ensure test that I added to use the new test harness. Once this is in I'll submit the rename PR and another to gps. |
@carolynvs, how intuitive or painful was the new test harness? Always looking for feedback |
@tro3 I figured it out in about 1 minute. 🍰 💖 One thing I wondered about was if there was a place to put a "this is what this test case is about" type comment? e.g. in the testcase.json I wanted to put in the equivalent of "Make sure the -n (dry-run) flag doesn't actually do stuff OR things". |
@tro3 I'm a goof. What I meant to say is "THANK YOU for making the tests more straightforward. It was intuitive to switch over and made the tests easier to grok." |
On the testcase doc, a |
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.
Mostly small things again, but we're making progress. (Cost of a big PR 😞 )
@@ -25,7 +26,7 @@ func IsRegular(name string) (bool, error) { | |||
return false, err | |||
} | |||
if fi.IsDir() { | |||
return false, fmt.Errorf("%q is a directory, should be a file", name) | |||
return false, errors.Errorf("%q is a directory, should be a file", name) |
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.
These errs changes aren't really related - next time, let's separate unrelated things into their own PR.
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 about that! I'll make sure to split these up better next time. Thank you for your patience.
@@ -134,6 +118,29 @@ func (l *Lock) MarshalJSON() ([]byte, error) { | |||
return buf.Bytes(), err | |||
} | |||
|
|||
// TODO(carolynvs) this should be moved to gps |
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 make an issue for this. (I'm not quite fully convinced, but still, an issue is the thing to do.)
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 would only need to move to gps
if the lock diffing implementation was moved to gps
as it's used when doing the diff. Here's an issue to discuss if we really want to move it and how. #312
} | ||
|
||
// ShouldNotExist returns an error if path exists. | ||
func (h *Helper) ShouldNotExist(path string) error { |
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 not sure about this pattern - Should{Not}Exist()
vs. Must{Not}Exist()
. It seems to mirror the distinction between testing.T.Error()
and testing.T.Fatal()
, but it actually doesn't, because here it's left up to the caller to actually set an error. I think that might be misleading.
I can see why you're doing it this way - you want to be able to compose it with higher-order calls. But let's compose these differently - have the function that returns the error be a non-exported one, called by all of these helper methods.
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 came from the discussion in #vendor on how to improve the tests and their output when a test fails. The suggestions which resonated with me was to not fail a test from a helper when it is a condition that the test was directing verifying, and to return an error instead. On the flip side, if the error is not something that a test is explicitly testing (e.g. some unrelated problem deep in the test setup), then failing with a stack trace is fine.
For example, consider a test that is asserting that the lock file doesn't exist:
-
When the helper fails the test, a more generic error message is output along with a stack trace. Then you must walk back through the stack trace to figure out which line in the test failed.
$ go test -run TestStuff --- FAIL: TestStuff (0.00s) test.go:64: lock.json should not exist github.com/golang/dep.TestStuff /Users/caro8994/src/github.com/golang/dep/txn_writer_test.go:21 testing.tRunner /usr/local/Cellar/go/1.8/libexec/src/testing/testing.go:657 runtime.goexit /usr/local/Cellar/go/1.8/libexec/src/runtime/asm_amd64.s:2197
-
When the helper returns an error that is checked by the test, the error message and offending line in the test is more clear.
--- FAIL: TestStuff (0.00s) txn_writer_test.go:441: The lock file should not be written during a dry-run
My preference is to export and use the functions that return an error (what I was naming Should*
) and limit the use of the original Must*
functions which fail the test from the helper.
Let me know if the problem is ambiguous name of the function, or if it is the testing pattern (returning errors) and I will update the PR accordingly.
Again, my apologies for putting this test refactoring in the PR. At the time I asked you about whether or not it should be split out, I think we both underestimated how much of a change it would be. 😃
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.
Hah, yes, totally. I'd forgotten about both that conversation, and consideration, despite being there for it 😄 Sorry, yes, let's carry on as-is.
h.Cd(pc.Project.AbsRoot) | ||
h.Setenv("GOPATH", pc.tempDir) | ||
|
||
// Setup a Source Manager |
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.
nit: "setup" is a noun, "set up" is the verb phrase :)
) | ||
|
||
// TestProjectContext groups together test project files and helps test them | ||
type TestProjectContext 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.
Seems like we have some overlap here with the new harness from #287. Could be that this is actually completely complementary to what's there, though. Could you provide some quick context?
OK, I think we're ready to go ahead with this. Everything else I had was a nit or a question...so, onward! |
Wooo! 😄 Thank you for your patience and help getting this figured out! |
Respect dry run flag during dep ensure
This fixes #145.
When I first looked at this, turning on
-v
by default when-n
is present, seemed like an easy win. However the verbose output from thegps
solver wasn't really what I was looking for in a dry-run, I wanted to know what ensure would do, not what it evaluated. So I added output which clearly shows what will be written to the filesystem when the command is run without-n
.