Skip to content

Commit

Permalink
Allow allowing new files (#846)
Browse files Browse the repository at this point in the history
  • Loading branch information
xalvarez authored Jan 17, 2024
1 parent a816be9 commit ee75107
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 77 deletions.
127 changes: 74 additions & 53 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,55 +1,76 @@
{
"plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"eslint-comments/no-use": "off",
"i18n-text/no-en": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"env": {
"node": true,
"es6": true,
"jest/globals": true
}
"plugins": [
"jest",
"@typescript-eslint"
],
"extends": [
"plugin:github/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"eslint-comments/no-use": "off",
"i18n-text/no-en": "off",
"import/no-namespace": "off",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "no-public"
}
],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/ban-ts-comment": "error",
"camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],
"@typescript-eslint/func-call-spacing": [
"error",
"never"
],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"@typescript-eslint/semi": [
"error",
"never"
],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"env": {
"node": true,
"es6": true,
"jest/globals": true
}
}
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ jobs:
uses: ./
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
pattern: .*json
trustedAuthors: xalvarez, dependabot[bot]
pattern: .*example
trustedAuthors: xalvarez
allowNewFiles: false
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Syntax:
githubToken: ${{ secrets.GITHUB_TOKEN }}
pattern: .*\.example
trustedAuthors: xalvarez
allowNewFiles: false
```

The action has the following inputs:
Expand All @@ -25,8 +26,11 @@ The action has the following inputs:
`.*\.example` would match any file with the `.example` extension.
* `trustedAuthors`: (**Optional**) A comma-separated list of GitHub usernames. If a pull request is
opened by any of these authors, the action will not fail even if the pull request modifies a file
that matches the pattern. This is useful for allowing certain trusted authors to make changes to
protected files.
that matches the pattern.
* `allowNewFiles`: (**Optional**) A boolean value that determines whether new files that match the
pattern should be allowed in the pull request. If set to `true`, the action will not fail even if
a new file that matches the pattern is added in the pull request. If not provided or set to
`false`, the action will fail if a new file that matches the pattern is added.

> [!IMPORTANT]
> This Action supports pull request events only.
Expand Down
7 changes: 5 additions & 2 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ jest.mock('../src/pattern-matcher')

describe('main', () => {
const trustedAuthors = 'exampleAuthor1,exampleAuthor2'
const changedFiles = [{filename: 'exampleFile1.md'}, {filename: 'exampleFile2.md'}]
const changedFiles = [
{filename: 'exampleFile1.md', status: 'added'},
{filename: 'exampleFile2.md', status: 'modified'}
]
const pattern = 'examplePattern'

let isTrustedAuthorSpy: jest.SpyInstance
Expand Down Expand Up @@ -62,7 +65,7 @@ describe('main', () => {
await run()

expect(getChangedFilesSpy).toHaveBeenCalledWith('exampleOwner', 'exampleOwner/exampleRepo', 1)
expect(checkChangedFilesAgainstPatternSpy).toHaveBeenCalledWith(changedFiles, pattern)
expect(checkChangedFilesAgainstPatternSpy).toHaveBeenCalledWith(changedFiles, pattern, false)
expect(core.setFailed).not.toHaveBeenCalled()
})

Expand Down
15 changes: 14 additions & 1 deletion __tests__/pattern-matcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,21 @@ describe('pattern-matcher', () => {
expect(core.setFailed).not.toHaveBeenCalled()
expect(core.debug).toHaveBeenCalledWith(`This commit doesn't contain any files`)
})

it('Should not reject matching added file when allowNewFiles is true', async () => {
const files: IFile[] = givenFiles()
const pattern = '.*.js'

await checkChangedFilesAgainstPattern(files, pattern, true)

expect(core.setFailed).not.toHaveBeenCalled()
expect(core.debug).toHaveBeenCalledWith(`There isn't any file matching the pattern ${pattern}`)
})
})

function givenFiles(): IFile[] {
return [{filename: 'src/file1.js'}, {filename: 'README.md'}]
return [
{filename: 'src/file1.js', status: 'added'},
{filename: 'README.md', status: 'modified'}
]
}
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ inputs:
trustedAuthors:
required: false
description: 'Always trust pull request authors included in this comma separated list, e.g.: user1, user2, user3'
allowNewFiles:
required: false
description: 'Allow matching files to be included in pull requests'
runs:
using: 'node20'
main: 'dist/index.js'
23 changes: 17 additions & 6 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prevent-file-change",
"version": "1.5.1",
"version": "1.6.0",
"private": true,
"description": "Fail a pull request workflow if certain files are changed",
"main": "lib/main.js",
Expand Down
3 changes: 2 additions & 1 deletion src/github-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {getOctokit} from '@actions/github'

export interface IFile {
filename: string
status: string
}

export default class GitHubService {
Expand All @@ -21,7 +22,7 @@ export default class GitHubService {

const files: IFile[] = []
for (const file of responseBody) {
files.push({filename: file.filename} as IFile)
files.push({filename: file.filename, status: file.status} as IFile)
}

core.debug(`Pull request ${pullRequestNumber} includes following files: ${JSON.stringify(files)}`)
Expand Down
3 changes: 2 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export async function run(): Promise<void> {
pullRequestNumber
)
const pattern: string = core.getInput('pattern', {required: true})
await checkChangedFilesAgainstPattern(files, pattern)
const allowNewFiles: boolean = 'true' === core.getInput('allowNewFiles')
await checkChangedFilesAgainstPattern(files, pattern, allowNewFiles)
} else {
core.setFailed('Pull request number is missing in github event payload')
}
Expand Down
22 changes: 18 additions & 4 deletions src/pattern-matcher.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import * as core from '@actions/core'
import {IFile} from './github-service'

export async function checkChangedFilesAgainstPattern(files: IFile[], pattern: string): Promise<void> {
export async function checkChangedFilesAgainstPattern(
files: IFile[],
pattern: string,
allowNewFiles = false
): Promise<void> {
if (files.length > 0) {
const regExp = new RegExp(pattern)
files.some(file => regExp.test(file.filename))
? core.setFailed(`There is at least one file matching the pattern ${pattern}`)
: core.debug(`There isn't any file matching the pattern ${pattern}`)
const shouldPreventFileChange = files.some(file => {
const isPatternMatched = regExp.test(file.filename)
if (isPatternMatched && allowNewFiles && file.status === 'added') {
return false
}
return isPatternMatched
})

if (shouldPreventFileChange) {
core.setFailed(`There is at least one file matching the pattern ${pattern}`)
} else {
core.debug(`There isn't any file matching the pattern ${pattern}`)
}
} else core.debug(`This commit doesn't contain any files`)
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
},
"exclude": ["node_modules", "**/*.test.ts", "**/*.config.ts"]
"exclude": ["node_modules", "__tests__", "**/*.config.ts"]
}

0 comments on commit ee75107

Please sign in to comment.