Skip to content
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

Dest symlinks #246

Merged
merged 8 commits into from
Jun 23, 2017
Merged

Dest symlinks #246

merged 8 commits into from
Jun 23, 2017

Conversation

phated
Copy link
Member

@phated phated commented Jun 18, 2017

@erikkemperman does this address your thoughts from your last comment in #239? vfs.symlink doesn't add the .symlink property yet (probably a separate thing to address).

A side thought: the relative option should probably renamed to relativeSymlinks for the vfs.dest method.

Copy link
Member

@erikkemperman erikkemperman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few questions and comments -- but mostly this is pretty much what I was trying to do.

type: 'boolean',
default: function(file) {
var isDirectory = file.isDirectory();
return (isWindows && isDirectory);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the default should just be a boolean value, set to isWindows? We have to AND the resolved option with isDirectory(file) per file anyway, because we probably shouldn't assume that the useJunctions option provided by the user always satisfies that restriction. Does that make sense?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this should be a separate PR though, as it's not as simple as changing the default

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All right, that makes sense.

Copy link
Member

@erikkemperman erikkemperman Jun 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, isn't it as simple as changing the default? Seems to me that the non-directory case will be forced to symtype 'file' anyway, and the call to file.isDirectory() will have to be done in write-contents/write-symbolic-link in any event, so no need to do it twice?

Sorry if I'm missing something obvious, it's late here and been one of those days :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to introduce isDirectory into the relative branch, I think.

@@ -12,6 +12,11 @@ var fo = require('../../file-operations');
function writeContents(optResolver) {

function writeFile(file, enc, callback) {
// Write it as a symlink
if (file.symlink) {
Copy link
Member

@erikkemperman erikkemperman Jun 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure here if the criterion should be existence of file.symlink and/or file.isSymbolic(). Ideally, if vinyl-fs is consistent with adding the symlink property, it should not matter I guess. But as things stand, the file.symlink test only catches the vinyls as produced by src() with falsy resolveSymlinks option (for this file), I believe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm nervous about using the isSymbolic() method within vinyl-fs methods because I tried to use it in some other code I was experimenting with and it wasn't acting as I thought it would. I found out this was due to not having a valid stat before I used it but I think checking this property is a better way to handle it because isSymbolic() can be true without having this property (I think?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I believe file.isSymbolic() returns true if and only if file.stats && file.stats.isSymbolicLink(). So that doesn't work for stat-less files.

Relying on symlink here should be fine, I think, but like I mentioned earlier I think that property should be attached in more places than is currently being done.

I had that figured out already though so I could quickly make a PR for that once this one is merged?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that sounds great. I should be merging this tomorrow.

});
var srcPath = file.symlink;

var isDirectory = file.isDirectory();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that the vinyls ending up here currently (excepting the mocked ones you added) would be piped in from src() with the resolveSymlinks set to false. But those vinyls will have the stats of the link, and thus file.isDirectory here is not actually testing whether the target is a directory but the link (and of course isDirectory() will always return false for stats that are isSymbolic()).

Actually I think this is a problem in the implementation of symlink() as well: we can't be sure the vinyl has the stats of the target at the moment we need to know if the target is a directory. In fact we just do new fs.Stats() if the vinyl doesn't have stats, in symlink/prepare...

I suppose we should do an extra fs.stat on the target path, here and in symlink -- but only if we really need to know if it's a directory (i.e. on Windows and with the useJunctions option set to true)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

People can inject whatever they want in the middle of the pipeline, so they could do anything like what I've mocked (they can even use a different Vinyl source). We can't couple dest to src in that way and are better with a more lax, yet slightly leaky abstraction (with the .symlink property and usage of fs.Stats).

That's a bit rambling; let me know if I should further clarify.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my remark wasn't very clear :-)

I agree about the loose coupling, but my point here is that I think the isDirectory call here is wrong in the common case of getting a symbolic vinyl as produced by src() with resolveSymlinks set to false!

Because it is looking at the wrong stats in that case, namely those of the link and not the target, and then isDirectory will return false even though the target might be a directory, and as a consequence the symlink type should've been'junction' but now won't be.

Does that help?

If you change the test for this, or add a new one, to work with an actually sourced symlink rather than one with a mock isDirectory()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... I think you will see what I mean. (Sorry, hit the button prematurely)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Downloading the suggested patch so sending this now)

@erikkemperman The following example shows something a user might do that would require us to handle this case:

vfs.src('./my-giant-image-dir')
  .pipe(through(function toSymlink(file, _, cb) {
    // Since a directory was sourced, file.isDirectory() will be true and we'll want to create a junction on Windows
    file.symlink = file.path;
  })
  .pipe(vfs.dest('./my-output-location'))

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll give that a shot.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proved that Windows does not handle things properly if you force 'junction' at https://ci.appveyor.com/project/gulpjs/vinyl-fs/build/487

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think using another fs.stat is proper because the Vinyl object doesn't need to be produced from the filesystem, so that call would only be correct in some circumstances (yet still incorrect in others).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but it would treat correctly it least the cases where it has the necessary information to, or so it seems to me.

It's too bad that node doesn't enforce this for us, but good to have that confirmed at least.

Having read up a little bit on junctions I'm wondering if the current approach, where it'd be the default even, may end up causing more problems than it solves?

As far as I can tell the problem was that symlinking dirs (but not files?) requires admin privileges.

But junctions are dangerous in that they may cause users to lose data (c.f. the caveats in the comments).

Then again that's easy for me to say because I don't use Windows.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

npm exclusively uses junctions on Windows, so I think the caveats were over-cautious.


onWritten();
});
var srcPath = file.symlink;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this ought to be called target, as in the fs.symlink call? Personally I think source is a bit confusing in this context, I think of this as the destination of the link :-)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a semantic standpoint, you are correct. But naming it target inverts my expectation (as someone that doesn't link very often). I'm fine with altering naming conventions (with proper documentation comments) but I don't want to hold up release of 3.0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, although I'm not sure what you mean by documenting it?

The variable has no external visibility and is only used briefly within the innermost scopes of dest/write-contents/write-symbolic-link and symlink/index?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a code comment explaining what "target" means in the context.

type: symType,
};

fo.symlink(srcPath, file.path, opts, onWritten);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to deduplicate the junction voodoo and do it within fo.symlink, now that we have that separate function which is called from both dest() and symlink()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but I'd rather ship this last feature to get 3.0 released and work on de-duplication in a future release.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All right, yeah it shouldn't be a breaking change.

@stephenlacy
Copy link

@phated I cloned to the branch and ran the tests, several failed for me on Linux. All were to do with the filesystem returning errors. I can provide detailed information as required.

@phated
Copy link
Member Author

phated commented Jun 20, 2017

@stevelacy These all pass on travis, so I am wondering what is different on your system. Not sure if it belongs in this PR and might be something we look at in person

@erikkemperman
Copy link
Member

erikkemperman commented Jun 21, 2017

@stevelacy Were your errors resolved now? I ask because, after nuking node_modules and doing a fresh yarn install I suddenly also get errors on previously working code. And both Travis and Appveyor fail now, even on master:

Will take a quick look to see if I can figure out what is going on.

EDIT The common theme of the failing tests is that errors are no longer being propagated correctly, but unfortunately I have no time right now to look into this further.

@stephenlacy
Copy link

@erikkemperman @phated yes, those are exactly my errors. I could take a look at them once I get some free time

@phated
Copy link
Member Author

phated commented Jun 21, 2017

Seeing these on OSX too when deps updated. Investigating

@phated
Copy link
Member Author

phated commented Jun 21, 2017

This looks like a problem with readable-stream because it seems to be the only dep that updated

@phated
Copy link
Member Author

phated commented Jun 21, 2017

@stevelacy @erikkemperman this is definitely a problem introduced in readable-stream as backported from node 8. I'm working with them to get it fixed.

@erikkemperman
Copy link
Member

@phated Thanks for the update.

FYI I'm trying to get an extra test done this evening if I find some time, that might clarify the problem I believe still remains with isDirectory() in this PR.

@phated
Copy link
Member Author

phated commented Jun 21, 2017

@erikkemperman I'm pretty clear on what you are saying but you are thinking of it from an integration point-of-view. I will post an example of why we can't make those assumptions (after I deal with this nodejs/streams issue).

@erikkemperman
Copy link
Member

@phated All right, I'll hang back for a bit :-)

@stephenlacy
Copy link

All tests passing on node 8.1.2 now

@phated
Copy link
Member Author

phated commented Jun 23, 2017

@erikkemperman I believe I've created issues for the 4 outstanding items in this PR. I think we should work on them in smaller chunks so I've merged this and we can work from there.

@erikkemperman
Copy link
Member

@phated Yup, thanks!

phated added a commit that referenced this pull request Nov 27, 2017
phated added a commit that referenced this pull request Nov 27, 2017
phated added a commit that referenced this pull request Nov 27, 2017
phated added a commit that referenced this pull request Nov 27, 2017
phated added a commit that referenced this pull request Nov 27, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 28, 2017
phated added a commit that referenced this pull request Nov 30, 2017
phated added a commit that referenced this pull request Nov 30, 2017
phated added a commit that referenced this pull request Nov 30, 2017
phated added a commit that referenced this pull request Nov 30, 2017
phated added a commit that referenced this pull request Nov 30, 2017
phated added a commit that referenced this pull request Dec 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants