Skip to content

Commit

Permalink
Add 'trustedAuthors' flag
Browse files Browse the repository at this point in the history
  • Loading branch information
xalvarez committed Dec 4, 2021
1 parent 8ff7355 commit 94995a8
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 38 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ jobs:
uses: ./
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
pattern: .*.example
pattern: .*json
trustedAuthors: xalvarez, dependabot
2 changes: 1 addition & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"printWidth": 80,
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": false,
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Syntax:
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
pattern: .*.example
trustedAuthors: xalvarez
```

Where `pattern` is a valid JavaScript regular expression matching filenames (including path) of files which must not be changed.
Expand Down
30 changes: 30 additions & 0 deletions __tests__/author-checker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {isTrustedAuthor} from '../src/author-checker'

describe('author-checker', () => {
it('Should allow trusted author', async () => {
const pullRequestAuthor = 'examplePullRequestAuthor1'
const trustedAuthors = 'examplePullRequestAuthor1, examplePullRequestAuthor2'

const result = await isTrustedAuthor(pullRequestAuthor, trustedAuthors)

expect(result).toBeTruthy()
})

it('Should reject not trusted author', async () => {
const pullRequestAuthor = 'examplePullRequestAuthor1'
const trustedAuthors = 'examplePullRequestAuthor2, examplePullRequestAuthor3'

const result = await isTrustedAuthor(pullRequestAuthor, trustedAuthors)

expect(result).toBeFalsy()
})

it('Should reject empty trustedAuthors', async () => {
const pullRequestAuthor = 'examplePullRequestAuthor'
const trustedAuthors = ''

const result = await isTrustedAuthor(pullRequestAuthor, trustedAuthors)

expect(result).toBeFalsy()
})
})
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ inputs:
pattern:
required: true
description: 'JavaScript regular expression matching filenames (including path) of files which must not be changed'
trustedAuthors:
required: false
description: 'Always trust pull request authors includes in this comma separated list, e.g.: user1, user2, user3'
runs:
using: 'node12'
main: 'dist/index.js'
44 changes: 41 additions & 3 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.

2 changes: 1 addition & 1 deletion 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.0.3",
"version": "1.1.0",
"private": true,
"description": "Fail a pull request workflow if certain files are changed",
"main": "lib/main.js",
Expand Down
6 changes: 6 additions & 0 deletions src/author-checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export async function isTrustedAuthor(pullRequestAuthor: string, trustedAuthors: string): Promise<boolean> {
if (trustedAuthors) {
const authors: string[] = trustedAuthors.split(',').map((author: string) => author.trim())
return authors.includes(pullRequestAuthor)
} else return false
}
25 changes: 7 additions & 18 deletions src/github-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,19 @@ export default class GitHubService {
this.octokit = getOctokit(gitHubToken)
}

async getChangedFiles(
repositoryOwner: string,
repositoryName: string,
pullRequestNumber: number
): Promise<IFile[]> {
const responseBody = await this.octokit.paginate(
this.octokit.rest.pulls.listFiles,
{
owner: repositoryOwner,
repo: repositoryName,
pull_number: pullRequestNumber
}
)
async getChangedFiles(repositoryOwner: string, repositoryName: string, pullRequestNumber: number): Promise<IFile[]> {
const responseBody = await this.octokit.paginate(this.octokit.rest.pulls.listFiles, {
owner: repositoryOwner,
repo: repositoryName,
pull_number: pullRequestNumber
})

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

core.debug(
`Pull request ${pullRequestNumber} includes following files: ${JSON.stringify(
files
)}`
)
core.debug(`Pull request ${pullRequestNumber} includes following files: ${JSON.stringify(files)}`)

return files
}
Expand Down
19 changes: 11 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,34 @@ import * as core from '@actions/core'
import GitHubService, {IFile} from './github-service'
import PatternMatcher from './pattern-matcher'
import {context} from '@actions/github'
import {isTrustedAuthor} from './author-checker'

async function run(): Promise<void> {
try {
const trustedAuthors: string = core.getInput('trustedAuthors')
const pullRequestAuthor: string = context.actor
const eventName: string = context.eventName
if (eventName === 'pull_request') {
const githubToken: string = core.getInput('githubToken')
core.debug(`Event='${eventName}', Author='${pullRequestAuthor}', Trusted Authors='${trustedAuthors}'`)
if (await isTrustedAuthor(pullRequestAuthor, trustedAuthors)) {
core.info(`${pullRequestAuthor} is a trusted author and is allowed to modify any matching files.`)
} else if (eventName === 'pull_request') {
const githubToken: string = core.getInput('githubToken', {required: true})
const gitHubService = new GitHubService(githubToken)
const pullRequestNumber: number =
context.payload?.pull_request?.number || 0
const pullRequestNumber: number = context.payload?.pull_request?.number || 0
if (pullRequestNumber) {
const files: IFile[] = await gitHubService.getChangedFiles(
context.repo.owner,
context.repo.repo,
pullRequestNumber
)
const pattern: string = core.getInput('pattern')
const pattern: string = core.getInput('pattern', {required: true})
const patternMatcher = new PatternMatcher()
await patternMatcher.checkChangedFilesAgainstPattern(files, pattern)
} else {
core.setFailed('Pull request number is missing in github event payload')
}
} else {
core.setFailed(
`Only pull_request events are supported. Event was: ${eventName}`
)
core.setFailed(`Only pull_request events are supported. Event was: ${eventName}`)
}
} catch (error: unknown) {
if (error instanceof Error) {
Expand Down
5 changes: 1 addition & 4 deletions src/pattern-matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import * as core from '@actions/core'
import {IFile} from './github-service'

export default class PatternMatcher {
async checkChangedFilesAgainstPattern(
files: IFile[],
pattern: string
): Promise<void> {
async checkChangedFilesAgainstPattern(files: IFile[], pattern: string): Promise<void> {
if (files?.length > 0) {
const regExp = new RegExp(pattern)
files.some(file => regExp.test(file.filename))
Expand Down

0 comments on commit 94995a8

Please sign in to comment.