-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
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
fix(core): throw error if build folder already exists on initial clean #9112
Conversation
✅ [V2]Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify site configuration. |
⚡️ Lighthouse report for the deploy preview of this PR
|
@@ -30,6 +30,7 @@ | |||
// More context: https://github.com/facebook/docusaurus/pull/1839 | |||
|
|||
import path from 'path'; | |||
import * as fs from '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.
Use fs-extra
and await fs.pathExists()
; you can find other examples.
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.
@Josh-Cena I'm pushing a commit that use await pathExists instead of sync func
@thedevwonder Using the sync version is fine here, since we'd better leave the method sync. It shouldn't be too much of a problem. |
sorry I didn't see the notification and already did it. Should I revert back to sync version? |
if (this.initialClean) { | ||
return; | ||
} | ||
|
||
if (await fs.pathExists(this.outputPath)) { |
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 outputPath
is supposed to exist—this method is used to clean the output. What's problematic is if it's not a directory. You also need to test if it's a file.
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.
Right, I will add a validation for file as well
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.
@Josh-Cena I have a confusion here, pathExistsSync will still return true when build
is a file. Then why do we need an additional validation?
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 are three cases here:
outputPath
doesn't exist -> Always good;removeFiles
does nothingoutputPath
is a directory -> This is why we have this function; we want to clean its content and prepare for the next build outputoutputPath
is a file -> This is what Build error Thecwd
option must be a path to a directory when a file calledbuild
exists #9110 is reporting. In this casedelSync
fails becausecwd
is a file, not a directory. Logically, we can't clear anything from this file, so we fail.
We don't want to touch the behavior in cases 1 and 2, only give a better error in 3.
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 see, I misunderstood earlier. We do not need to change the behaviour when this.outputPath
is a directory. So the check that I've put should only validate files and not directories.
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.
@Josh-Cena Reading the original issue, I came across another observation. the error message should occur in case-insensitive filesystems as well. To check if there are no existing files with same name as build
but different case, I created a private validator that returns boolean. checks for both files and directories with duplicate name. This will solve:
- case where root dir may have diff variations of same file such as
BUILD
,build
,buiLD
. - diff variations of same directories.
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.
tested the code in local env.
…ase-insensitive systems
private checkIfFileNameIsDuplicate(): boolean { | ||
const dir = path.dirname(this.outputPath); | ||
const baseName = path.basename(this.outputPath).toLowerCase(); | ||
// eslint-disable-next-line no-restricted-properties | ||
const duplicateFiles = fs | ||
.readdirSync(dir) | ||
.filter((fileName) => fileName.toLowerCase() === baseName); | ||
if (duplicateFiles.length > 0) { | ||
return true; | ||
} | ||
return false; | ||
} |
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 should be enough
if (
(await fs.pathExists(outputPath)) &&
(await fs.stat(outputPath)).isFile()
) {
throw new Error("...");
}
On:
- case insensitive FS: this should throw for files like
BUILD
orbuild
- case-sensitive FS: if a file named
BUILD
exists, I think it's allowed to create a folder namedbuild
? 🤔
I only have access to a case insensitive file-system. Can someone test my assumption on case sensitive file-system?
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.
@slorber, I think yours is better because it doesn't break the abstraction of whether the FS is case-sensitive or not :)
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 should be enough
if ( (await fs.pathExists(outputPath)) && (await fs.stat(outputPath)).isFile() ) { throw new Error("..."); }On:
- case insensitive FS: this should throw for files like
BUILD
orbuild
- case-sensitive FS: if a file named
BUILD
exists, I think it's allowed to create a folder namedbuild
? 🤔I only have access to a case insensitive file-system. Can someone test my assumption on case sensitive file-system?
I used gitpod.io to test this usecase. It is a case-sensitive system. I changed the code because I thought someone using case-sensitive FS may create a BUILD
file and someone not using this FS may find this file throwing an error during the build
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 do you think about this, any suggestions? @slorber @Josh-Cena
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's fine. Running the same project on different systems can often run into inconsistencies, like some imports that work on case-insensitive systems suddenly fail in case-sensitive 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.
@Josh-Cena I see, keeping it simple then!
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.
@Josh-Cena @slorber I have used the sync version of pathExists
and stat
functions and fixed the error message. Let me know if this looks good.
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 have also tested the code in my local env. Works fine:)
@@ -152,6 +166,12 @@ export default class CleanWebpackPlugin { | |||
return; | |||
} | |||
|
|||
if (this.checkIfFileNameIsDuplicate()) { | |||
throw new Error( | |||
`Output directory ${this.outputPath} already exists. Docusaurus needs this directory to save the build output. Either remove the directory or choose a different build directory via '--out-dir'.`, |
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 it's a directory the role of this plugin is to delete. As @Josh-Cena said the problem is only if a file already exists, so this error message is not good.
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.
@slorber this makes sense, I'll do the change
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.
changes done
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, LGTM.
Unfortunate that we need to use sync IO but not a big deal.
@slorber is there some way we can check the performance diff in introducing sync I/O functions here? Just curious. Probably build time could be one parameter |
oh I see, build performance is literally one of the checks here, my bad |
That's fine, this CI fails for other reasons 👍 |
Pre-flight checklist
Motivation
Fix #9110
Test Plan
local tests
Test links
Deploy preview: https://deploy-preview-9112--docusaurus-2.netlify.app/
Related issues/PRs