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

Delete files not existent in project directory #6501

Merged
merged 6 commits into from
May 24, 2024

Conversation

DavidMLPalma
Copy link
Contributor

Fix #4120 : When renaming scenes/extensions/external events/external layouts, duplicate files exist, one with the old name and one with the new name. Similarly, when deleting this items, the respective files are also not removed from the project directory. This is due to the fact that when saving the project, only the existing files in the project editor are written and saved, and nothing more is done. There may still remain old files that should not be there, potentially creating duplicates and confusion. Therefore, when saving, we can resolve this issue by deleting all files from the scenes/extensions/external events/external layouts directories, before writing the correct files.

DavidMLPalma and others added 2 commits April 3, 2024 15:19
…al layouts , duplicate files exist.

When renaming scenes/extensions/external events/external layouts,
duplicate files exist, one with the old name and one with the new name.
When deleting this items, the respective files are also not removed from
the project directory. This is due to the fact that when saving the
project, only the existing files in the project editor are written and saved, and the old ones
still remain, potentialy creating duplicates and confusion. Therefore, when saving,
we can resolve this issue by deleting all files from the
scenes/extensions/external events/external layouts directories, before
writing the correct files.
@DavidMLPalma DavidMLPalma requested a review from 4ian as a code owner April 3, 2024 20:55
@AlexandreSi
Copy link
Collaborator

Hi @DavidMLPalma,
Rhanks for the suggested PR!

I feel like this strategy is a bit brutal. We already have issues with users saving their projects in the installation directory and losing their projects because of that (when GDevelop is updated). So I think it's better not to have another potential cause for losing files.

May I suggest that you check if the directory name is in the list splittedProjectFolderNames in LocalProjectWriter?

@DavidMLPalma
Copy link
Contributor Author

Hi @AlexandreSi ,
Thanks for your reply!

I totally get your point, and in fact by checking if the directory name is one of those present in the list may lead to a more secure solution. I will change it then.

@@ -142,6 +168,12 @@ export const onSaveProject = (
};

const projectPath = path.dirname(filePath);

deleteExistingFilesFromDirs(projectPath).catch(err => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you improve a bit the whole onSaveProject function so that it's an async function? (If you're not familiar with it, I can provide guidance).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I improve the deleteExistingFilesFromDirs function so it's an async functions as well?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, I think it's okay to keep it synchronous

@@ -142,6 +168,12 @@ export const onSaveProject = (
};

const projectPath = path.dirname(filePath);

deleteExistingFilesFromDirs(projectPath).catch(err => {
console.error('Unable to delete files in the project:', err);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think here we could gain clarity if you mention the context: Unable to clean project folder before saving project:

//Project file
if (entries.length === 1) resolve();

//If multiFile enabled in settings and directories exist!
Copy link
Collaborator

Choose a reason for hiding this comment

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

You do not actually check if multifile is enabled in the project's settings. You should use project.isFolderProject() for that.

Copy link
Collaborator

Choose a reason for hiding this comment

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

To make it one step clearer by removing the indent, you could do this:

if (!project.isFolderProject()) return;

const entries = fs.readdirSync(projectPath);
...

Also, I'm not sure this comment is useful anymore, your code is quite clear.

filenames.forEach(file => {
let result = dirPath.concat('\\', file);
fs.unlink(result, err => {
if (err) return reject(err);
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could add context here, such as:

throw new Error(`Unable to remove file ${file}: ${err.message}`

Or something like that

@DavidMLPalma
Copy link
Contributor Author

Hi @AlexandreSi ,
Thanks a lot for the patience and feedback! I will try to implement all the things you pointed out !

if (project.isFolderProject()) {
//If multiFile enabled in settings and directories exist!
entries.forEach(entry => {
let dirPath = projectPath.concat('\\', entry);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I forgot to check how you handle paths: Using strings that you concat yourself won't work because Windows user \ separator while Linux and macOS user /. So you should use path.join instead. You can find other users of path in the codebase.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Furthermore, you can use const instead of let since you never reassign the variable value

let dirPath = projectPath.concat('\\', entry);
if (
fs.statSync(dirPath).isDirectory() &&
splittedProjectFolderNames.includes(entry)
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could use this condition as an early return as well:

if (splittedProjectFolderNames.includes(entry)) return;
let dirPath = projectPath.concat('\\', entry);
...

) {
const filenames = fs.readdirSync(dirPath);
filenames.forEach(file => {
let result = dirPath.concat('\\', file);
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could rename result with a name that actually represents what it is: filePath or fileToRemovePath

@DavidMLPalma
Copy link
Contributor Author

Hi @AlexandreSi ,

Once again, thanks for your help and consideration !

try {
deleteExistingFilesFromDirs(project, projectPath);
} catch (e) {
return Promise.reject(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that deleting the existing files from the folders should fail silently.
You can use instead:
console.warn('Unable to clean project folder before saving project: ', error)
But stopping a save could mean preventing a user from saving if an error happens.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, since you made the change to an async function (thank you!), could you replace the return new Promise.reject line 158 by a throw new Error('...')

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done !

if (!splittedProjectFolderNames.includes(entry)) return;

const dirPath = path.join(projectPath, entry);
if (fs.statSync(dirPath).isDirectory()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can also replace the added intent by an early return here.

Copy link
Collaborator

@AlexandreSi AlexandreSi left a comment

Choose a reason for hiding this comment

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

LGTM!
Thanks for staying with me though I asked for a lot of changes. I hope our coding style is clearer to you today!

@AlexandreSi AlexandreSi merged commit eaebc16 into 4ian:master May 24, 2024
3 checks passed
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.

Renaming scenes/external events creates duplicate files in the project directory
2 participants