Skip to content
7 changes: 7 additions & 0 deletions .github/nitpicks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,10 @@
blocking: true
pathFilter:
- '+**/*.dll'

- markdown: |
Testing - fail if the file doesn't include "!always()"
pathFilter:
- '.github/workflows/example.yml'
contentFilter:
- '!(!always())'
22 changes: 22 additions & 0 deletions .github/workflows/example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Build
on:
pull_request:
push:
branches:
- master
- 'releases/*'

jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1

- run: npm ci
- run: npm run all
- name: nitpicker
uses: ./
with:
nitpicks: './.github/nitpicks.yml'
token: '${{ secrets.GITHUB_TOKEN }}'
if: 'always()'
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ A comment's `contentFilter` parameter accepts an array of regular expressions th
- '(\+\s*debugger)'
```

If you would like nitpicker to only post a comment when the regular expression is _not_ found, you can use a `!` in front of the contentFilter. For example:

```yaml
- markdown: |
Please ensure that all .ts files include the word 'unicorn!'
pathFilter:
- 'src/**/*.ts'
contentFilter:
- '!(unicorn)'
```

## Development

Install the dependencies
Expand Down
8 changes: 8 additions & 0 deletions __tests__/data/negated_content_filter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- markdown: |
## Notice
Thanks for this contribution. We _really_ appreciate your help!

- markdown: |
## Blocking issue
contentFilter:
- '!(\+\s*console.log\(.*\))'
92 changes: 92 additions & 0 deletions __tests__/services/state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,98 @@ test('getMatchingContentChanges returns single changes with multiple matching co
expect(result).toHaveLength(1);
});

test('getMatchingContentChanges returns single changes with negated contentFilter', () => {
const comment: Comment = {
pathFilter: ['*'],
markdown: 'Everything',
blocking: false,
contentFilter: [`!(\\+\\s*debugger)`]
};

const changes: Change[] = [
{
file: 'src/services/configReader.ts',
changeType: ChangeType.edit,
patch:
"@@ -4,14 +4,19 @@ import { safeLoad } from 'js-yaml';\n" +
" import { Constants } from '../constants';\n" +
' \n' +
' export function getConfiguredComments(filePath: string): Comment[] {\n' +
'+ if (!fs.existsSync(filePath)) {\n' +
'+ throw Error(`Nitpicks file does not exist: ${filePath}`);\n' +
'+ }\n' +
'+\n' +
" const contents = fs.readFileSync(filePath, 'utf8');\n" +
' const yaml: Comment[] = safeLoad(contents);\n' +
' \n' +
'+ nope!\n' +
'+ console.log("this shouldnt have been committed!")\n' +
' \n' +
' const comments: Comment[] = yaml\n' +
'- .filter(y => y.markdown && y.pathFilter?.length > 0)\n' +
'+ .filter(y => y.markdown)\n' +
' .map(c => ({\n' +
' ...c,\n' +
"- markdown: `${c.markdown}${c.blocking ? Constants.BlockingText : ''}`\n" +
"+ markdown: `${c.markdown}${c.blocking ? Constants.BlockingText : ''}`,\n" +
'+ pathFilter: c.pathFilter ?? Constants.DefaultPathFilter // Default to match everything\n' +
' }));\n' +
' \n' +
' return comments;'
}
];

const result = getMatchingContentChanges(comment, changes);

expect(result).toHaveLength(1);
});

test('getMatchingContentChanges returns single changes with a doubly negated contentFilter', () => {
const comment: Comment = {
pathFilter: ['*'],
markdown: 'Everything',
blocking: false,
contentFilter: [`!(!false)`]
};

const changes: Change[] = [
{
file: 'src/services/configReader.ts',
changeType: ChangeType.edit,
patch:
"@@ -4,14 +4,19 @@ import { safeLoad } from 'js-yaml';\n" +
" import { Constants } from '../constants';\n" +
' \n' +
' export function getConfiguredComments(filePath: string): Comment[] {\n' +
'+ if (!fs.existsSync(filePath)) {\n' +
'+ throw Error(`Nitpicks file does not exist: ${filePath}`);\n' +
'+ }\n' +
'+\n' +
" const contents = fs.readFileSync(filePath, 'utf8');\n" +
' const yaml: Comment[] = safeLoad(contents);\n' +
' \n' +
'+ nope\n' +
'+ console.log("this shouldnt have been committed!")\n' +
' \n' +
' const comments: Comment[] = yaml\n' +
'- .filter(y => y.markdown && y.pathFilter?.length > 0)\n' +
'+ .filter(y => y.markdown)\n' +
' .map(c => ({\n' +
' ...c,\n' +
"- markdown: `${c.markdown}${c.blocking ? Constants.BlockingText : ''}`\n" +
"+ markdown: `${c.markdown}${c.blocking ? Constants.BlockingText : ''}`,\n" +
'+ pathFilter: c.pathFilter ?? Constants.DefaultPathFilter // Default to match everything\n' +
' }));\n' +
' \n' +
' return comments;'
}
];

const result = getMatchingContentChanges(comment, changes);

expect(result).toHaveLength(1);
});

test('getMatchingContentChanges throws error if unable to parse regexpr', () => {
const comment: Comment = {
pathFilter: ['*'],
Expand Down
Loading