-
Notifications
You must be signed in to change notification settings - Fork 29.7k
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
Make recursive rmdir more strict #35250
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.
mostly just questions, but I approve the intent here
try { | ||
stats = statSync(path); | ||
} catch (err) { | ||
if (err.code === 'ENOENT') |
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.
what is swallowed here if not ENOENT
?
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.
rimraf
swallows all kinds of errors and throwing all errors from stat
caused lots of failures.
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 looks like you'd get into trouble here if L198 threw, because stats
would then be undefined, and the call to isDirectory()
on L204 would fail... it doesn't seem recoverable?
assert.strictEqual(err.name, 'TypeError'); | ||
assert.match(err.message, /^The argument 'path' is not a directory\./); | ||
})); | ||
fs.unlinkSync(filePath); |
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.
imo this should be guaranteed to be called--not sure if it is. also: don't know offhand if other tests try to guarantee this or just leave crap all over the fs
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 can use a try/finally to clean this up.
lib/internal/fs/rimraf.js
Outdated
} | ||
|
||
if (!stats.isDirectory()) { | ||
throw new ERR_INVALID_ARG_VALUE('path', path, 'is not a directory'); |
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 the correct error to throw? unsure of the intent of ERR_INVALID_ARG_VALUE
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 that it is. I wanted to throw an ENOTDIR
error but I'm not sure how to do that. I don't see it happening anywhere else in the code. I tried making a regular Error
and setting the code to ENOTDIR
but the linter didn't like 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.
There doesn't seem to be a convenience function for this error; maybe we should create one.
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.
@iansu in internal/errors.js
, I think we want to add:
E('ENOTDIR',
'no such file or directory',
SystemError);
You'd then instantiate it something like this:
new SystemError('ENOTDIR', {syscall: 'rmdir', code: 'ENOTDIR', path: './', message: 'whatever error message rimraf throws'', errno: whatever error code rmdir has})
Basically the ideal is that someone would be able to look at err.code
both in the recursive and non recursive rmdir
, and use the same logic.
Edit: I mean ENOTDIR.
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.
whoops, sorry I mean ENOTDIR
, but I think it should be similar logic.
Is there a reason this is done at the top level, instead of making changes to the logic in |
This is not semver-major because cc @nodejs/tsc |
@RyanZim That was my initial thought but it turns out that both of the existing rimraf functions (now named |
@RyanZim @iansu I think I'd be content with a |
try { | ||
stats = statSync(path); | ||
} catch (err) { | ||
if (err.code === 'ENOENT') |
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 looks like you'd get into trouble here if L198 threw, because stats
would then be undefined, and the call to isDirectory()
on L204 would fail... it doesn't seem recoverable?
assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); | ||
assert.strictEqual(err.name, 'TypeError'); | ||
assert.match(err.message, /^The argument 'path' is not a directory\./); | ||
fs.unlinkSync(filePath); |
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 needs a try/finally
as well because these assert
calls may throw
lib/internal/fs/rimraf.js
Outdated
} | ||
|
||
if (!stats.isDirectory()) { | ||
throw new ERR_INVALID_ARG_VALUE('path', path, 'is not a directory'); |
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.
@iansu in internal/errors.js
, I think we want to add:
E('ENOTDIR',
'no such file or directory',
SystemError);
You'd then instantiate it something like this:
new SystemError('ENOTDIR', {syscall: 'rmdir', code: 'ENOTDIR', path: './', message: 'whatever error message rimraf throws'', errno: whatever error code rmdir has})
Basically the ideal is that someone would be able to look at err.code
both in the recursive and non recursive rmdir
, and use the same logic.
Edit: I mean ENOTDIR.
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 it would be good to address Chris' comments too 👍 but, to me, once tests pass, this is looking 99% of the way there.
I believe I did address Chris' comments in my last two commits. |
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 for the patch @iansu 👍
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 am -1 on Throws an exception when the path to remove does not exist
@Trott to clarify, it are you advocating we'd keep the existing |
+1 for this. |
I'm OK either way, but if it was left up to me, I'd prefer that we made the default behavior stricter and introduce a loose option. |
I too would prefer the stricter behavior (throw on missing paths, and on attempt to remove file). @gengjiawen what if we compromised by adding a
I would love for us to figure out a compromise that works for folks, my fear is that we'll continue keeping |
It seems like we are going over the discussion in #35171 all over again. While it is important to find the best solution, I too wish this is not dragged out ad infinitum. From @bcoe's table above, it is clear that there is no universal solution here. The design of various languages should only be treated as inspiration and not canon. At best, POSIX syscalls is the closest thing we can consider as being canonical to node fs. I, for one, would have presumed that there is no contradiction between having a remove function for file, for directories and for everything. Even POSIX has
This would have been ideal. But didn't we conclude this would be too disruptive for existing |
It feels like we're having trouble reaching a compromise here, I know it's not the ideal we were picturing, but I'm thinking perhaps we do go with @Trott's suggestion of a Then we update #35171 accordingly to reflect this. Plan of action I propose:
|
@nodejs/tsc I believe the TSC could help unblock the
CC: @Trott |
I am personally in favour of making |
A little bit of a heads up that I'm wondering if we can come up with a better name than |
@bencoe I'm also +1 on having strict be the default behavior and follow the previously proposed analogy:
|
@bcoe My larger question remains unanswered: How would you (and do you plan to?) expose all the features of POSIX Please reach a consensus on this before finalizing the PRs. |
If we are changing our minds and changing |
@ruyadorno @CxRes
I'd like to suggest we call this out of scope for the current goal, which is getting This won't prevent us from adding additional APIs in the future. |
I agree with this (with a flag). But I want the default behavior to be tolerant on ENOENT (#35250 (comment)). I think pragmatism is mostly important in this case. Maybe we can start a user poll in twitter to get more comprehensive community feedback ? |
(Sorry, in advance, for the upcoming bold but I feel strongly about what I am saying below but it is meant with the best of intentions): But it does affect how we will add additional API in the future, hence imperative that it is considered right now. We have discussed two options:
Clarity on this question today, taking the pain today, will mean we do not snowball the poorly considered API choice which we intend to fix as best we can! Once this is resolved either way, it is so for the life of node js!!!! As I have stated earlier, I do not want to drag this either but any decision must take into account the consequences. And I just want to make sure that everyone has fully weighed the future consequence (not just present) of both these choices before this is finalized. |
@gengjiawen I don't feel strongly about default behavior. Where I do feel strongly, is that when This would make it clear that
@CxRes you've done a good job of expressing your point of view, and I haven't gotten the impression that it's being dismissed. Node.js collaborators such as @gengjiawen, are making the case that they would rather continue to see this functionality be part of I am a -1 on introducing an additional recursive method (not based on this being a bad idea, but based on it revisiting a decision we reached consensus on; I don't feel the design we landed on innately bad). I am deferring the decision about default behavior of rmdir to the TSC. |
@bcoe I did not want to suggest that I was being dismissed, and I apologize if that is the impression, my concern was primarily that implications of either decisions were being fully considered. It was unclear until your last post, in particular, that there is consensus in the community to add all recursive functionality in Once this feature lands, I do intend to open another issue to add all POSIX |
@CxRes 👍 I strongly encourage the @nodejs/tsc to consider your point of view, which you've summarized well. What's important to me, is that we take steps towards removing the |
Based on the conversation with the TSC today (see: https://github.com/nodejs/TSC/pull/934/files#diff-331798040b102838053b8a643840013e):
@iansu is going to make an effort to send a patch with the alias, I believe at that time we should warn about the deprecation in logs -- our hope is to the this landed in time for In |
Let me try and get this straight since the TSC language is so abstract (I'll use terrible function names so as not bias an eventual choice):
Do I follow this correctly??? 😕 |
@CxRes that is correct while the exact versions for the deprecation (doc-only / runtime) and removal might be different. |
Unfortunately I wasn't at the TSC meeting, but I find it weird to mark an API stable knowing that we are going to deprecate it. |
@targos I found this compromise reasonable. The point of calling Meanwhile, folks in the @nodejs/tooling group are working to get a PR made for the new Edit: It's also worth noting that we're not deprecating the
I think you've summarized the plan of action well. I'm ultimately a strong 👍 for this approach, because it unblocks an argument that's been going in circles for months, in such a way that's minimally disruptive to users that have already adopted the recursive behavior of |
Closing in favour of #35494 |
This PR changes the behaviour of recursive rmdir (rimraf) in the following ways:
These changes are based on the discussion in #35171
cc @nodejs/tooling
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes