Skip to content

Commit

Permalink
Allow closing pull request instead of exiting with error (#1065)
Browse files Browse the repository at this point in the history
* fix naming

* add option to close PR

* fix tests and build

* fix PR issues and tests

* revert githubservice renaming

* Add negative test

* Improve documentation

* Remove duplicate input

---------

Co-authored-by: Xavier Alvarez <[email protected]>
  • Loading branch information
HasanHajHasan and xalvarez authored Dec 29, 2024
1 parent 9490285 commit 267c166
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 16 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ The action has the following inputs:
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.
- `closePR`: (**Optional**) A boolean value that determines whether the pull request should be closed
if a pattern match is found. If set to `true`, the action will close the pull request instead of failing.
If not provided or set to `false`, the action will fail if a pattern match is found.

> [!IMPORTANT]
> This Action supports pull_request and pull_request_target events only.
Expand Down
18 changes: 17 additions & 1 deletion __tests__/github-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ jest.mock('@actions/github', () => ({
paginate: jest.fn().mockResolvedValue([{filename: 'exampleFile1.md'}, {filename: 'exampleFile2.md'}]),
rest: {
pulls: {
listFiles: jest.fn()
listFiles: jest.fn(),
update: jest.fn()
}
}
})
Expand Down Expand Up @@ -48,4 +49,19 @@ describe('github-service', () => {
)
expect(changedFiles).toEqual(expectedFiles)
})

it('Should close pull request', async () => {
const owner = 'exampleOwner'
const repo = 'exampleRepo'
const pullRequestNumber = 1

await gitHubService.closePullRequest(owner, repo, pullRequestNumber)

expect(octokit.getOctokit(GITHUB_TOKEN).rest.pulls.update).toHaveBeenCalledWith({
owner: owner,
repo: repo,
pull_number: pullRequestNumber,
state: 'closed'
})
})
})
14 changes: 12 additions & 2 deletions __tests__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ describe('main', () => {
return trustedAuthors
case 'pattern':
return pattern
case 'githubToken':
return 'exampleToken'
default:
return ''
}
})

isTrustedAuthorSpy = jest.spyOn(authorChecker, 'isTrustedAuthor').mockResolvedValue(false)

context.eventName = 'pull_request'
context.payload = {
pull_request: {
Expand All @@ -65,7 +66,16 @@ describe('main', () => {
await run()

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

Expand Down
33 changes: 28 additions & 5 deletions __tests__/pattern-matcher.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import * as core from '@actions/core'
import {IFile} from '../src/github-service'
import GitHubService, {IFile} from '../src/github-service'
import {checkChangedFilesAgainstPattern} from '../src/pattern-matcher'

const GITHUB_TOKEN = 'exampleGitHubToken'

jest.mock('@actions/core')
jest.mock('../src/github-service')

describe('pattern-matcher', () => {
let gitHubService: GitHubService
let closePullRequestSpy: jest.SpyInstance

beforeEach(() => {
gitHubService = new GitHubService(GITHUB_TOKEN)
closePullRequestSpy = jest.spyOn(GitHubService.prototype, 'closePullRequest').mockResolvedValue()
})
afterEach(() => {
jest.restoreAllMocks()
})
Expand All @@ -13,40 +23,53 @@ describe('pattern-matcher', () => {
const files: IFile[] = givenFiles()
const pattern = '.*.js'

await checkChangedFilesAgainstPattern(files, pattern)
await checkChangedFilesAgainstPattern(files, pattern, gitHubService, 'exampleRepo', 'exampleOwner', 1, false)

expect(core.setFailed).toHaveBeenCalledWith(`There is at least one file matching the pattern ${pattern}`)
expect(core.debug).not.toHaveBeenCalled()
expect(closePullRequestSpy).not.toHaveBeenCalled()
})

it('Should not reject non matching pattern', async () => {
const files: IFile[] = givenFiles()
const pattern = '.*.ts'

await checkChangedFilesAgainstPattern(files, pattern)
await checkChangedFilesAgainstPattern(files, pattern, gitHubService, 'exampleRepo', 'exampleOwner', 1, false)

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

it('Should not reject empty commit', async () => {
const files: IFile[] = []
const pattern = '.*'

await checkChangedFilesAgainstPattern(files, pattern)
await checkChangedFilesAgainstPattern(files, pattern, gitHubService, 'exampleRepo', 'exampleOwner', 1, false)

expect(core.setFailed).not.toHaveBeenCalled()
expect(core.debug).toHaveBeenCalledWith(`This commit doesn't contain any files`)
expect(closePullRequestSpy).not.toHaveBeenCalled()
})

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

await checkChangedFilesAgainstPattern(files, pattern, true)
await checkChangedFilesAgainstPattern(files, pattern, gitHubService, 'exampleRepo', 'exampleOwner', 1, false, true)

expect(core.setFailed).not.toHaveBeenCalled()
expect(core.debug).toHaveBeenCalledWith(`There isn't any file matching the pattern ${pattern}`)
expect(closePullRequestSpy).not.toHaveBeenCalled()
})
it('Should close PR', async () => {
const files: IFile[] = givenFiles()
const pattern = '.*.js'

await checkChangedFilesAgainstPattern(files, pattern, gitHubService, 'exampleRepo', 'exampleOwner', 1, true)

expect(core.setFailed).not.toHaveBeenCalled()
expect(closePullRequestSpy).toHaveBeenCalledWith('exampleOwner', 'exampleRepo', 1)
})
})

Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ inputs:
allowNewFiles:
required: false
description: 'Allow matching files to be included in pull requests'
closePR:
required: false
description: 'If set to true, the action will close the pull request if a pattern match is found. By default, the action will fail if a pattern match is found.'
runs:
using: 'node20'
main: 'dist/index.js'
20 changes: 17 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.

9 changes: 9 additions & 0 deletions src/github-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,13 @@ export default class GitHubService {

return files
}

async closePullRequest(owner: string, repo: string, pullRequestNumber: number): Promise<void> {
await this.octokit.rest.pulls.update({
owner: owner,
repo: repo,
pull_number: pullRequestNumber,
state: 'closed'
})
}
}
12 changes: 11 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,17 @@ export async function run(): Promise<void> {
)
const pattern: string = core.getInput('pattern', {required: true})
const allowNewFiles: boolean = 'true' === core.getInput('allowNewFiles')
await checkChangedFilesAgainstPattern(files, pattern, allowNewFiles)
const closePR: boolean = core.getInput('closePR') === 'true'
await checkChangedFilesAgainstPattern(
files,
pattern,
gitHubService,
context.repo.repo,
context.repo.owner,
pullRequestNumber,
closePR,
allowNewFiles
)
} else {
core.setFailed('Pull request number is missing in github event payload')
}
Expand Down
14 changes: 11 additions & 3 deletions src/pattern-matcher.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import * as core from '@actions/core'
import {IFile} from './github-service'
import GitHubService, {IFile} from './github-service'

export async function checkChangedFilesAgainstPattern(
files: IFile[],
pattern: string,
githubService: GitHubService,
repo: string,
owner: string,
pullRequestNumber: number,
closePR: boolean,
allowNewFiles = false
): Promise<void> {
if (files.length > 0) {
Expand All @@ -15,9 +20,12 @@ export async function checkChangedFilesAgainstPattern(
}
return isPatternMatched
})

if (shouldPreventFileChange) {
core.setFailed(`There is at least one file matching the pattern ${pattern}`)
if (closePR) {
await githubService.closePullRequest(owner, repo, pullRequestNumber)
} else {
core.setFailed(`There is at least one file matching the pattern ${pattern}`)
}
} else {
core.debug(`There isn't any file matching the pattern ${pattern}`)
}
Expand Down

0 comments on commit 267c166

Please sign in to comment.