A .sqlfluffignore in a subdirectory is ignored one level down when the path is relative
Search before asking
- I searched the issues and found no similar issues.
What Happened
A .sqlfluffignore file in a subdirectory stops applying one level below itself when the path on the command line is relative. sqlfluff lint . lints the ignored files, and sqlfluff fix . rewrites them.
The same tree behaves correctly when you pass an absolute path.
Expected Behaviour
A .sqlfluffignore file applies to every file below its own directory, as docs/source/configuration/ignoring_configuration.rst describes. The result must not depend on whether the path argument is relative or absolute.
Observed Behaviour
sub/deeper/b.sql is linted and fixed, although sub/.sqlfluffignore holds *.sql.
$ sqlfluff lint .
== [sub/deeper/b.sql] FAIL
L: 1 | P: 7 | LT01 | Expected only single space before numeric literal. Found
| ' '. [layout.spacing]
L: 1 | P: 12 | LT01 | Expected only single space before 'from' keyword. Found
| ' '. [layout.spacing]
L: 1 | P: 15 | CP01 | Keywords must be consistently upper case.
| [capitalisation.keywords]
All Finished!
$ sqlfluff lint /tmp/sfign/proj
All Finished!sqlfluff fix . then writes the ignored file:
$ cat sub/deeper/b.sql
SELECT 3 from t
$ sqlfluff fix . --force
== [sub/deeper/b.sql] FIXED
3 fixable linting violations found
$ cat sub/deeper/b.sql
SELECT 3 FROM tThe cause is in src/sqlfluff/core/linter/discovery.py:199-203:
if not (
dirname == inner_dirname
or dirname.startswith(os.path.abspath(inner_dirname) + os.sep)
):
inner_ignore_specs.remove((inner_dirname, inner_file, inner_spec))dirname and inner_dirname both come from the same os.walk(path), so both are relative when path is relative. Only the right side is made absolute. The startswith test is then always false and the spec is dropped as soon as the walk descends one level. The dirname == inner_dirname branch still holds, so a file in the same directory as the ignore file is ignored correctly. Only the levels below it leak.
How to reproduce
mkdir -p /tmp/sfign/proj/sub/deeper
cd /tmp/sfign/proj
printf '[sqlfluff]\ndialect = ansi\n' > .sqlfluff
printf '*.sql\n' > sub/.sqlfluffignore
printf 'SELECT 1\n' > top.sql
printf 'SELECT 2\n' > sub/a.sql
printf 'SELECT 3 from t\n' > sub/deeper/b.sql
sqlfluff lint . # lists sub/deeper/b.sql, wrong
sqlfluff lint /tmp/sfign/proj # lists top.sql only, correctDirectly on the discovery function:
>>> from sqlfluff.core.linter.discovery import paths_from_path
>>> sorted(paths_from_path("."))
['sub/deeper/b.sql', 'top.sql']
>>> sorted(paths_from_path("/tmp/sfign/proj"))
['/tmp/sfign/proj/top.sql']sub/a.sql sits next to the ignore file and is ignored in both runs.
Dialect
ansi. The defect is in path discovery, so every dialect is affected.
Version
sqlfluff, version 4.3.0
Python 3.12.12Introduced in 16006dd0f (#6080).
Configuration
/tmp/sfign/proj/.sqlfluff:
[sqlfluff]
dialect = ansi/tmp/sfign/proj/sub/.sqlfluffignore:
*.sqlAre you willing to work on and submit a PR to address the issue?
- Yes I am willing to submit a PR!
Code of Conduct
- I agree to follow this project's Code of Conduct
Source: sqlfluff/sqlfluff