xojs/xo

EACCESS: permission denied on ignored directory

nicolas-goudry opened this issue · 2 comments

I have the following issue trying to run xo on my project :

> xo-eaccess-permission-denied-on-ignored-dir@1.0.0 lint
> xo

Error: EACCES: permission denied, scandir '/fakepath/xo-eaccess-permission-denied-on-ignored-dir/.data/mysql/#innodb_redo'

I created a repository to reproduce this issue.

To better explain, I have a MySQL container running alongside my Node.js API, and I’m mounting its data directory in a .data directory at my project root. I added the .data to .gitignore, expecting xo to actually ignore it, but it still tries to scan the subdirectories of .data, as per the error message.

Also tried adding .data/**/* to .gitignore, as well as adding them to the ignores xo config field, with same results.


EDIT: I know I can fix this by running something like this: sudo chgrp -R $(whoami) .data/mysql but I still think this is a bug.

I took a look at the code, and the error seems to come from the call to globby inside the globFiles function, on line 29. I’ll keep digging.

EDIT: tracked down to the call to fastGlob inside globby’s isIgnoredByIgnoreFiles function, on line 68. Still digging.

EDIT 2: got too deep, now I’m down to @nodelib/fs.walk used by fast-glob… I’m getting lost a bit rn. I don’t quite understand why the isIgnoredByIgnoreFiles is only passing **/.gitignore to fast-glob.

Here’s my walkthrough:

  1. in xo’s globFiles function, I can see that the following is passed to globby’s ignore option
[
  '**/node_modules/**',
  '**/bower_components/**',
  'flow-typed/**',
  'coverage/**',
  '{tmp,temp}/**',
  '**/*.min.js',
  'vendor/**',
  'dist/**',
  'tap-snapshots/*.{cjs,js}',
  '**/.data'
]
  1. in globby’s getFilter function, I can see that the following options are passed
{
  ignore: [
    '**/node_modules/**',
    '**/bower_components/**',
    'flow-typed/**',
    'coverage/**',
    '{tmp,temp}/**',
    '**/*.min.js',
    'vendor/**',
    'dist/**',
    'tap-snapshots/*.{cjs,js}',
    '**/.data'
  ],
  gitignore: true,
  absolute: true,
  cwd: '/home/nicolas/dev/kdaf-api',
  expandDirectories: true
}
  1. in globby’s getIgnoreFilesPatterns function, I can see that the field ignoreFiles is destructured from the options object although it doesn’t exist
  2. therefore, the resulting ignoreFilesPatterns of getFilter is the following:
[ '**/.gitignore' ]

I think this is a bug, but somehow, if I change the code of getIgnoreFilesPatterns to destructure the right field from options (ie. the ignore field), I get the following for ignoreFilesPatterns:

[
  '**/node_modules/**',
  '**/bower_components/**',
  'flow-typed/**',
  'coverage/**',
  '{tmp,temp}/**',
  '**/*.min.js',
  'vendor/**',
  'dist/**',
  'tap-snapshots/*.{cjs,js}',
  '**/.data',
  '**/.gitignore'
]

But that doesn’t resolve the issue…

It’s getting late and I’m tired rn, I’ll try to continue tomorrow.

Looking back at this, I now think I understand what does the isIgnoredByIgnoreFiles function: return a list of ignore files relative to the current working directory.

I think this is the issue, because it’s actually trying to search for an ignore file inside the .data directory, to which it doesn’t have access permission.

If I update the code of isIgnoredByIgnoreFiles to add the options.ignore to the ignoreFilesGlobOptions.ignore, the issue goes away.

Something like that (dirty):

export const isIgnoredByIgnoreFiles = async (patterns, options) => {
	const {cwd, suppressErrors, deep} = normalizeOptions(options);

	const paths = await fastGlob(patterns, {
		cwd,
		suppressErrors,
		deep,
		...ignoreFilesGlobOptions,
		ignore: [...ignoreFilesGlobOptions.ignore, ...options.ignore],
	});

	const files = await Promise.all(
		paths.map(async filePath => ({
			filePath,
			content: await fs.promises.readFile(filePath, 'utf8'),
		})),
	);

	return getIsIgnoredPredicate(files, cwd);
};

Would you like me to open an issue in globby’s repository instead ? I opened a related issue in globby’s repository.