Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added rebase detection #17

Merged
merged 7 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,27 @@ jobs:
uses: dailydevops/dependamerge-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
command: squash
command: squash # Not required, default is squash
approve-only: false # Not required, default is false
handle-submodule: false # Not required, default is false
handle-dependency-group: true # Not required, default is true
target: patch # Not required, default is patch
skip-commit-verification: false # Not required, default is false
skip-verification: false # Not required, default is false
```

### Inputs

| Name | Description | Required | Default | Available Values |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | ----------------------------- | -------------------------------- |
| `token` | GitHub token | ✔ | `${{ secrets.GITHUB_TOKEN }}` | --- |
| `command` | Merge Method with which the pull request is to be merged. | ❌ | `squash` | `squash`, `merge` |
| `approve-only` | If `true`, then the pull request is only approved, but not merged. | ❌ | `false` | `true`, `false` |
| `handle-submodule` | If `true`, Git submodules are also merged. | ❌ | `false` | `true`, `false` |
| `handle-dependency-group` | If `true`, all pull requests of a dependency group are merged. | ❌ | `true` | `true`, `false` |
| `target` | The maximum target of the version comparison to be merged. | ❌ | `patch` | `major`, `minor`, `patch`, `any` |
| `skip-commit-verification` | If `true`, then the action will not expect the commits to have a verification signature. It is required to set this to true in GitHub Enterprise Server. | ❌ | `false` | `true`, `false` |
| `skip-verification` | If `true`, the action will not validate the user or the commit verification status. | ❌ | `false` | `true`, `false` |
| Name | Description | Required | Default | Available Values |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------: | ----------------------------- | -------------------------------- |
| `token` | GitHub token | ✔ | `${{ secrets.GITHUB_TOKEN }}` | --- |
| `command` | Merge Method with which the pull request is to be merged.<br/><br/>Command `squash` is the default command. All commits are squashed into one commit and merged into the target branch.<br />Command `merge` merges the pull request with the target branch, keeping the commit history.<br />Command `rebase` only checks whether the PR is behind the current compare branch, if so, the PR is rebased. | ❌ | `squash` | `squash`, `merge`, `rebase` |
| `approve-only` | If `true`, then the pull request is only approved, but not merged. | ❌ | `false` | `true`, `false` |
| `handle-submodule` | If `true`, Git submodules are also merged. | ❌ | `false` | `true`, `false` |
| `handle-dependency-group` | If `true`, all pull requests of a dependency group are merged. | ❌ | `true` | `true`, `false` |
| `target` | The maximum target of the version comparison to be merged. | ❌ | `patch` | `major`, `minor`, `patch`, `any` |
| `skip-commit-verification` | If `true`, then the action will not expect the commits to have a verification signature. It is required to set this to true in GitHub Enterprise Server. | ❌ | `false` | `true`, `false` |
| `skip-verification` | If `true`, the action will not validate the user or the commit verification status. | ❌ | `false` | `true`, `false` |

### Outputs

Expand Down
105 changes: 105 additions & 0 deletions __tests__/api.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
const {
addComment,
approvePullRequest,
comparePullRequest,
getPullRequest
} = require('../src/api')

// Mock for the GitHub API client
const github = {
rest: {
issues: {
createComment: jest.fn()
},
pulls: {
createReview: jest.fn()
},
repos: {
compare: jest.fn(),
get: jest.fn()
}
}
}

// Mock data for the repository and pull request
const repo = {
owner: {
login: 'test-owner'
},
name: 'test-repo'
}

const pullRequest = {
base: {
ref: 'base-branch'
},
number: 123,
head: {
ref: 'head-branch'
}
}

describe('Tests for `addComment` function', () => {
test('should add a comment to the pull request', async () => {
const body = 'This is a test comment.'

await addComment(github, repo, pullRequest, body)

expect(github.rest.issues.createComment).toHaveBeenCalledWith({
owner: repo.owner.login,
repo: repo.name,
issue_number: pullRequest.number,
body
})
})
test('should approve the pull request', async () => {
const body = 'This is a test review.'

await approvePullRequest(github, repo, pullRequest, body)

expect(github.rest.pulls.createReview).toHaveBeenCalledWith({
owner: repo.owner.login,
repo: repo.name,
pull_number: pullRequest.number,
event: 'APPROVE',
body
})
})
test('should compare a pull request in a GitHub repository', async () => {
const baseRef = 'base-branch'
const headRef = 'head-branch'

const comparisonResult = {
// Mock comparison result
}

github.rest.repos.compare = jest.fn().mockResolvedValue(comparisonResult)

const result = await comparePullRequest(github, repo, pullRequest)

expect(github.rest.repos.compare).toHaveBeenCalledWith({
owner: repo.owner.login,
repo: repo.name,
basehead: `${baseRef}...${headRef}`
})

expect(result).toBe(comparisonResult)
})
test('should retrieve a pull request from a GitHub repository', async () => {
const pullRequestData = {
// Mock pull request data
}

github.rest.pulls.get = jest.fn().mockResolvedValue(pullRequestData)

const result = await getPullRequest(github, repo, pullRequest)

expect(github.rest.pulls.get).toHaveBeenCalledWith({
owner: repo.owner.login,
repo: repo.name,
pull_number: pullRequest.number
})

expect(result).toBe(pullRequestData)
})
})
161 changes: 160 additions & 1 deletion __tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@ const { getInputs, state, validatePullRequest } = require('../src/utils')
// Mock for dependencies (in this case, for the GitHub "core" module)
jest.mock('@actions/core')

const basePullRequest = {
base: {
ref: ''
},
head: {
ref: ''
}
}

const mockCompare = jest.fn()
mockCompare.mockReturnValue({
data: {
status: 'before',
behind_by: 0
}
})

const mockGetPullRequest = jest.fn()
mockGetPullRequest.mockReturnValue({
data: {
mergeable: true
}
})
// Mock for the object `github` that is passed to the action
const github = {
rest: {
Expand All @@ -12,7 +35,10 @@ const github = {
},
pulls: {
createReview: jest.fn(),
get: jest.fn()
get: mockGetPullRequest
},
repos: {
compare: mockCompare
}
}
}
Expand All @@ -28,6 +54,7 @@ const repository = {
describe('Tests for `getInputs` function', () => {
test.each([
['merge', 'merge'],
['rebase', 'rebase'],
['squash', 'squash and merge'],
[undefined, 'squash and merge']
])('input command `%s` should return `%s`', (command, expected) => {
Expand Down Expand Up @@ -316,8 +343,136 @@ describe('Tests for `validatePullRequest` function', () => {
)
})

test('should return `true` after compare commits', async () => {
mockCompare.mockReturnValueOnce({
data: {
status: 'behind',
behind_by: 2
}
})

const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
user: {
login: 'dependabot[bot]'
},
mergeable: true,
mergeable_state: 'behind'
}

const config = {
inputs: {
approveOnly: false,
command: 'rebase',
handleSubmodule: true,
handleDependencyGroup: true
},
metadata: {}
}

const result = await validatePullRequest(
github,
repository,
pullRequest,
config
)

expect(result.execute).toBe(true)
expect(result.body).toBe('@dependabot rebase')
expect(result.validationState).toBe(state.rebased)
expect(result.validationMessage).toBe('The pull request will be rebased.')
})

test('should return `false` when compare commits is not behind', async () => {
mockCompare.mockReturnValueOnce({
data: {
status: 'clean',
behind_by: 0
}
})

const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
user: {
login: 'dependabot[bot]'
}
}

const config = {
inputs: {
approveOnly: false,
command: 'rebase',
handleSubmodule: true,
handleDependencyGroup: true
},
metadata: {}
}

const result = await validatePullRequest(
github,
repository,
pullRequest,
config
)

expect(result.execute).toBe(false)
expect(result.validationState).toBe(state.skipped)
expect(result.validationMessage).toBe(
`The pull request is not behind the target branch.`
)
}, 15000)

test('should return `true` when pull request has a mergeable state of `null`', async () => {
mockGetPullRequest.mockReturnValueOnce({
data: {
mergeable: null,
mergeable_state: 'behind'
}
})

const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
user: {
login: 'dependabot[bot]'
},
mergeable: null,
mergeable_state: 'behind'
}

const config = {
inputs: {
approveOnly: false,
handleSubmodule: true,
handleDependencyGroup: true
},
metadata: {}
}

const result = await validatePullRequest(
github,
repository,
pullRequest,
config
)

expect(result.execute).toBe(true)
expect(result.body).toBe('@dependabot rebase')
expect(result.validationState).toBe(state.rebased)
expect(result.validationMessage).toBe('The pull request will be rebased.')
}, 15000)

test('should return `true` when pull request has a mergeable state of `behind`', async () => {
const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
Expand Down Expand Up @@ -354,6 +509,7 @@ describe('Tests for `validatePullRequest` function', () => {
'should return `false` when pull request has a mergeable state of `%s`',
async mergeableState => {
const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
Expand Down Expand Up @@ -395,6 +551,7 @@ describe('Tests for `validatePullRequest` function', () => {
'should return `false` when pull request has a target `%s` and update type `%s`',
async (target, updateType) => {
const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
Expand Down Expand Up @@ -448,6 +605,7 @@ describe('Tests for `validatePullRequest` function', () => {
'should return `true` when pull request has a target `%s` and update type `%s` and command `%s`',
async (target, updateType, cmd) => {
const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
Expand Down Expand Up @@ -486,6 +644,7 @@ describe('Tests for `validatePullRequest` function', () => {

test('should return `true` when pull request has approve-only enabled', async () => {
const pullRequest = {
...basePullRequest,
merged: false,
state: 'open',
draft: false,
Expand Down
2 changes: 1 addition & 1 deletion badges/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading