Add pull request closed event (#2684)

- [x] updates docs
- [x] adjust UI
  - [x] show correct icon
  - [x] show correct link (to pr)
  - [x] add as option in secret edit
- [x] parse webhook
- [x] update tests
  - [x] github merged
  - [x] github closed
  - [x] gitea merged
  - [x] gitea closed
  - [x] bitbucket merged
  - [x] bitbucket closed
  - [x] gitlab merged
  - [x] gitlab closed

closes #286
This commit is contained in:
Anbraten 2023-12-26 19:22:52 +01:00 committed by GitHub
parent df73d2c475
commit f01ac3f0a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 2920 additions and 139 deletions

View file

@ -4396,6 +4396,7 @@ const docTemplate = `{
"enum": [ "enum": [
"push", "push",
"pull_request", "pull_request",
"pull_request_closed",
"tag", "tag",
"deployment", "deployment",
"cron", "cron",
@ -4404,6 +4405,7 @@ const docTemplate = `{
"x-enum-varnames": [ "x-enum-varnames": [
"EventPush", "EventPush",
"EventPull", "EventPull",
"EventPullClosed",
"EventTag", "EventTag",
"EventDeploy", "EventDeploy",
"EventCron", "EventCron",

View file

@ -32,6 +32,15 @@
- **Dependency**: [Workflows][Workflow] can depend on each other, and if possible, they are executed in parallel. - **Dependency**: [Workflows][Workflow] can depend on each other, and if possible, they are executed in parallel.
- **Status**: Status refers to the outcome of a step or [workflow][Workflow] after it has been executed, determined by the internal command exit code. At the end of a [workflow][Workflow], its status is sent to the [forge][Forge]. - **Status**: Status refers to the outcome of a step or [workflow][Workflow] after it has been executed, determined by the internal command exit code. At the end of a [workflow][Workflow], its status is sent to the [forge][Forge].
## Pipeline events
- `push`: A push event is triggered when a commit is pushed to a branch.
- `pull_request`: A pull request event is triggered when a pull request is opened or a new commit is pushed to it.
- `pull_request_closed`: A pull request closed event is triggered when a pull request is closed or merged.
- `tag`: A tag event is triggered when a tag is pushed.
- `manual`: A manual event is triggered when a user manually triggers a pipeline.
- `cron`: A cron event is triggered when a cron job is executed.
## Conventions ## Conventions
Sometimes there are multiple terms that can be used to describe something. This section lists the preferred terms to use in Woodpecker: Sometimes there are multiple terms that can be used to describe something. This section lists the preferred terms to use in Woodpecker:

View file

@ -263,7 +263,7 @@ when:
#### `event` #### `event`
Available events: `push`, `pull_request`, `tag`, `deployment`, `cron`, `manual` Available events: `push`, `pull_request`, `pull_request_closed`, `tag`, `deployment`, `cron`, `manual`
Execute a step if the build event is a `tag`: Execute a step if the build event is a `tag`:

View file

@ -66,11 +66,11 @@ This is the reference list of all environment variables available to your pipeli
| `CI_COMMIT_REF` | commit ref | | `CI_COMMIT_REF` | commit ref |
| `CI_COMMIT_REFSPEC` | commit ref spec | | `CI_COMMIT_REFSPEC` | commit ref spec |
| `CI_COMMIT_BRANCH` | commit branch (equals target branch for pull requests) | | `CI_COMMIT_BRANCH` | commit branch (equals target branch for pull requests) |
| `CI_COMMIT_SOURCE_BRANCH` | commit source branch (empty if event is not `pull_request`) | | `CI_COMMIT_SOURCE_BRANCH` | commit source branch (empty if event is not `pull_request` or `pull_request_closed`) |
| `CI_COMMIT_TARGET_BRANCH` | commit target branch (empty if event is not `pull_request`) | | `CI_COMMIT_TARGET_BRANCH` | commit target branch (empty if event is not `pull_request` or `pull_request_closed`) |
| `CI_COMMIT_TAG` | commit tag name (empty if event is not `tag`) | | `CI_COMMIT_TAG` | commit tag name (empty if event is not `tag`) |
| `CI_COMMIT_PULL_REQUEST` | commit pull request number (empty if event is not `pull_request`) | | `CI_COMMIT_PULL_REQUEST` | commit pull request number (empty if event is not `pull_request` or `pull_request_closed`) |
| `CI_COMMIT_PULL_REQUEST_LABELS` | labels assigned to pull request (empty if event is not `pull_request`) | | `CI_COMMIT_PULL_REQUEST_LABELS` | labels assigned to pull request (empty if event is not `pull_request` or `pull_request_closed`) |
| `CI_COMMIT_MESSAGE` | commit message | | `CI_COMMIT_MESSAGE` | commit message |
| `CI_COMMIT_AUTHOR` | commit author username | | `CI_COMMIT_AUTHOR` | commit author username |
| `CI_COMMIT_AUTHOR_EMAIL` | commit author email address | | `CI_COMMIT_AUTHOR_EMAIL` | commit author email address |
@ -78,10 +78,10 @@ This is the reference list of all environment variables available to your pipeli
| | **Current pipeline** | | | **Current pipeline** |
| `CI_PIPELINE_NUMBER` | pipeline number | | `CI_PIPELINE_NUMBER` | pipeline number |
| `CI_PIPELINE_PARENT` | number of parent pipeline | | `CI_PIPELINE_PARENT` | number of parent pipeline |
| `CI_PIPELINE_EVENT` | pipeline event (push, pull_request, tag, deployment, cron, manual) | | `CI_PIPELINE_EVENT` | pipeline event (see [pipeline events](../20-usage/15-terminiology/index.md#pipeline-events)) |
| `CI_PIPELINE_URL` | link to the web UI for the pipeline | | `CI_PIPELINE_URL` | link to the web UI for the pipeline |
| `CI_PIPELINE_FORGE_URL` | link to the forge's web UI for the commit(s) or tag that triggered the pipeline | | `CI_PIPELINE_FORGE_URL` | link to the forge's web UI for the commit(s) or tag that triggered the pipeline |
| `CI_PIPELINE_DEPLOY_TARGET` | pipeline deploy target for `deployment` events (ie production) | | `CI_PIPELINE_DEPLOY_TARGET` | pipeline deploy target for `deployment` events (i.e. production) |
| `CI_PIPELINE_STATUS` | pipeline status (success, failure) | | `CI_PIPELINE_STATUS` | pipeline status (success, failure) |
| `CI_PIPELINE_CREATED` | pipeline created UNIX timestamp | | `CI_PIPELINE_CREATED` | pipeline created UNIX timestamp |
| `CI_PIPELINE_STARTED` | pipeline started UNIX timestamp | | `CI_PIPELINE_STARTED` | pipeline started UNIX timestamp |
@ -111,7 +111,7 @@ This is the reference list of all environment variables available to your pipeli
| | **Previous pipeline** | | | **Previous pipeline** |
| `CI_PREV_PIPELINE_NUMBER` | previous pipeline number | | `CI_PREV_PIPELINE_NUMBER` | previous pipeline number |
| `CI_PREV_PIPELINE_PARENT` | previous pipeline number of parent pipeline | | `CI_PREV_PIPELINE_PARENT` | previous pipeline number of parent pipeline |
| `CI_PREV_PIPELINE_EVENT` | previous pipeline event (push, pull_request, tag, deployment) | | `CI_PREV_PIPELINE_EVENT` | previous pipeline event (see [pipeline events](../20-usage/15-terminiology/index.md#pipeline-events)) |
| `CI_PREV_PIPELINE_URL` | previous pipeline link in CI | | `CI_PREV_PIPELINE_URL` | previous pipeline link in CI |
| `CI_PREV_PIPELINE_FORGE_URL` | previous pipeline link to event in forge | | `CI_PREV_PIPELINE_FORGE_URL` | previous pipeline link to event in forge |
| `CI_PREV_PIPELINE_DEPLOY_TARGET` | previous pipeline deploy target for `deployment` events (ie production) | | `CI_PREV_PIPELINE_DEPLOY_TARGET` | previous pipeline deploy target for `deployment` events (ie production) |

View file

@ -16,12 +16,13 @@ package metadata
// Event types corresponding to scm hooks. // Event types corresponding to scm hooks.
const ( const (
EventPush = "push" EventPush = "push"
EventPull = "pull_request" EventPull = "pull_request"
EventTag = "tag" EventPullClosed = "pull_request_closed"
EventDeploy = "deployment" EventTag = "tag"
EventCron = "cron" EventDeploy = "deployment"
EventManual = "manual" EventCron = "cron"
EventManual = "manual"
) )
// Different ways to handle failure states // Different ways to handle failure states

View file

@ -394,7 +394,6 @@
}, },
"event": { "event": {
"description": "Read more: https://woodpecker-ci.org/docs/usage/pipeline-syntax#event", "description": "Read more: https://woodpecker-ci.org/docs/usage/pipeline-syntax#event",
"default": ["push", "pull_request", "tag", "deployment"],
"oneOf": [ "oneOf": [
{ {
"type": "array", "type": "array",
@ -494,7 +493,7 @@
} }
}, },
"event_enum": { "event_enum": {
"enum": ["push", "pull_request", "tag", "deployment", "cron", "manual"] "enum": ["push", "pull_request", "pull_request_closed", "tag", "deployment", "cron", "manual"]
}, },
"constraint_list": { "constraint_list": {
"oneOf": [ "oneOf": [

View file

@ -163,8 +163,13 @@ func convertWorkspace(from *internal.Workspace) *model.Team {
// convertPullHook is a helper function used to convert a Bitbucket pull request // convertPullHook is a helper function used to convert a Bitbucket pull request
// hook to the Woodpecker pipeline struct holding commit information. // hook to the Woodpecker pipeline struct holding commit information.
func convertPullHook(from *internal.PullRequestHook) *model.Pipeline { func convertPullHook(from *internal.PullRequestHook) *model.Pipeline {
event := model.EventPull
if from.PullRequest.State == stateClosed || from.PullRequest.State == stateDeclined {
event = model.EventPullClosed
}
return &model.Pipeline{ return &model.Pipeline{
Event: model.EventPull, Event: event,
Commit: from.PullRequest.Dest.Commit.Hash, Commit: from.PullRequest.Dest.Commit.Hash,
Ref: fmt.Sprintf("refs/heads/%s", from.PullRequest.Dest.Branch.Name), Ref: fmt.Sprintf("refs/heads/%s", from.PullRequest.Dest.Branch.Name),
Refspec: fmt.Sprintf("%s:%s", Refspec: fmt.Sprintf("%s:%s",

View file

@ -456,10 +456,587 @@ const HookPull = `
} }
` `
const HookMerged = ` const HookPullRequestMerged = `
{ {
"repository": {
"type": "repository",
"full_name": "anbraten/test-2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2"
},
"avatar": {
"href": "https://bytebucket.org/ravatar/%7B26554729-595f-47d1-aedd-302625cb4a97%7D?ts=default"
}
},
"name": "test-2",
"scm": "git",
"website": null,
"owner": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"workspace": {
"type": "workspace",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"name": "Anbraten",
"slug": "anbraten",
"links": {
"avatar": {
"href": "https://bitbucket.org/workspaces/anbraten/avatar/?ts=1651865281"
},
"html": {
"href": "https://bitbucket.org/anbraten/"
},
"self": {
"href": "https://api.bitbucket.org/2.0/workspaces/anbraten"
}
}
},
"is_private": true,
"project": {
"type": "project",
"key": "TEST",
"uuid": "{3fa6429f-95e1-4c5a-875c-1753abcd8ace}",
"name": "test",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/workspaces/anbraten/projects/TEST"
},
"html": {
"href": "https://bitbucket.org/anbraten/workspace/projects/TEST"
},
"avatar": {
"href": "https://bitbucket.org/account/user/anbraten/projects/TEST/avatar/32?ts=1690725373"
}
}
},
"uuid": "{26554729-595f-47d1-aedd-302625cb4a97}",
"parent": null
},
"actor": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"pullrequest": { "pullrequest": {
"state": "MERGED" "comment_count": 0,
"task_count": 0,
"type": "pullrequest",
"id": 1,
"title": "README.md created online with Bitbucket",
"description": "README.md created online with Bitbucket",
"rendered": {
"title": {
"type": "rendered",
"raw": "README.md created online with Bitbucket",
"markup": "markdown",
"html": "<p>README.md created online with Bitbucket</p>"
},
"description": {
"type": "rendered",
"raw": "README.md created online with Bitbucket",
"markup": "markdown",
"html": "<p>README.md created online with Bitbucket</p>"
}
},
"state": "MERGED",
"merge_commit": {
"type": "commit",
"hash": "006704dbeab2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/commit/006704dbeab2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/commits/006704dbeab2"
}
}
},
"close_source_branch": true,
"closed_by": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"author": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"reason": "",
"created_on": "2023-12-05T18:28:16.861881+00:00",
"updated_on": "2023-12-05T18:29:44.785393+00:00",
"destination": {
"branch": {
"name": "main"
},
"commit": {
"type": "commit",
"hash": "6c5f0bc9b2aa",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/commit/6c5f0bc9b2aa"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/commits/6c5f0bc9b2aa"
}
}
},
"repository": {
"type": "repository",
"full_name": "anbraten/test-2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2"
},
"avatar": {
"href": "https://bytebucket.org/ravatar/%7B26554729-595f-47d1-aedd-302625cb4a97%7D?ts=default"
}
},
"name": "test-2",
"uuid": "{26554729-595f-47d1-aedd-302625cb4a97}"
}
},
"source": {
"branch": {
"name": "patch-2"
},
"commit": {
"type": "commit",
"hash": "668218c13e04",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/commit/668218c13e04"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/commits/668218c13e04"
}
}
},
"repository": {
"type": "repository",
"full_name": "anbraten/test-2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2"
},
"avatar": {
"href": "https://bytebucket.org/ravatar/%7B26554729-595f-47d1-aedd-302625cb4a97%7D?ts=default"
}
},
"name": "test-2",
"uuid": "{26554729-595f-47d1-aedd-302625cb4a97}"
}
},
"reviewers": [],
"participants": [
{
"type": "participant",
"user": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"role": "PARTICIPANT",
"approved": true,
"state": "approved",
"participated_on": "2023-12-05T18:29:25.611876+00:00"
}
],
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/pull-requests/1"
},
"commits": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/commits"
},
"approve": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/approve"
},
"request-changes": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/request-changes"
},
"diff": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/diff/anbraten/test-2:668218c13e04%0D6c5f0bc9b2aa?from_pullrequest_id=1&topic=true"
},
"diffstat": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/diffstat/anbraten/test-2:668218c13e04%0D6c5f0bc9b2aa?from_pullrequest_id=1&topic=true"
},
"comments": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/comments"
},
"activity": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/activity"
},
"merge": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/merge"
},
"decline": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/decline"
},
"statuses": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/1/statuses"
}
},
"summary": {
"type": "rendered",
"raw": "README.md created online with Bitbucket",
"markup": "markdown",
"html": "<p>README.md created online with Bitbucket</p>"
}
}
}
`
const HookPullRequestDeclined = `
{
"repository": {
"type": "repository",
"full_name": "anbraten/test-2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2"
},
"avatar": {
"href": "https://bytebucket.org/ravatar/%7B26554729-595f-47d1-aedd-302625cb4a97%7D?ts=default"
}
},
"name": "test-2",
"scm": "git",
"website": null,
"owner": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"workspace": {
"type": "workspace",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"name": "Anbraten",
"slug": "anbraten",
"links": {
"avatar": {
"href": "https://bitbucket.org/workspaces/anbraten/avatar/?ts=1651865281"
},
"html": {
"href": "https://bitbucket.org/anbraten/"
},
"self": {
"href": "https://api.bitbucket.org/2.0/workspaces/anbraten"
}
}
},
"is_private": true,
"project": {
"type": "project",
"key": "TEST",
"uuid": "{3fa6429f-95e1-4c5a-875c-1753abcd8ace}",
"name": "test",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/workspaces/anbraten/projects/TEST"
},
"html": {
"href": "https://bitbucket.org/anbraten/workspace/projects/TEST"
},
"avatar": {
"href": "https://bitbucket.org/account/user/anbraten/projects/TEST/avatar/32?ts=1690725373"
}
}
},
"uuid": "{26554729-595f-47d1-aedd-302625cb4a97}",
"parent": null
},
"actor": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"pullrequest": {
"comment_count": 0,
"task_count": 0,
"type": "pullrequest",
"id": 2,
"title": "CHANGELOG.md created online with Bitbucket",
"description": "CHANGELOG.md created online with Bitbucket",
"rendered": {
"title": {
"type": "rendered",
"raw": "CHANGELOG.md created online with Bitbucket",
"markup": "markdown",
"html": "<p>CHANGELOG.md created online with Bitbucket</p>"
},
"description": {
"type": "rendered",
"raw": "CHANGELOG.md created online with Bitbucket",
"markup": "markdown",
"html": "<p>CHANGELOG.md created online with Bitbucket</p>"
}
},
"state": "DECLINED",
"merge_commit": null,
"close_source_branch": false,
"closed_by": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"author": {
"display_name": "Anbraten",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/users/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D"
},
"avatar": {
"href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e/784add1f-95cc-42a5-a562-38a0e12de4fa/128"
},
"html": {
"href": "https://bitbucket.org/%7Bb1b7beef-77ca-452d-b059-fa092504ebd7%7D/"
}
},
"type": "user",
"uuid": "{b1b7beef-77ca-452d-b059-fa092504ebd7}",
"account_id": "70121:3046ad5f-946f-48fa-bcb4-a399eef48f0e",
"nickname": "Anbraten"
},
"reason": "",
"created_on": "2023-12-05T18:36:27.667680+00:00",
"updated_on": "2023-12-05T18:36:57.260672+00:00",
"destination": {
"branch": {
"name": "main"
},
"commit": {
"type": "commit",
"hash": "006704dbeab2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/commit/006704dbeab2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/commits/006704dbeab2"
}
}
},
"repository": {
"type": "repository",
"full_name": "anbraten/test-2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2"
},
"avatar": {
"href": "https://bytebucket.org/ravatar/%7B26554729-595f-47d1-aedd-302625cb4a97%7D?ts=default"
}
},
"name": "test-2",
"uuid": "{26554729-595f-47d1-aedd-302625cb4a97}"
}
},
"source": {
"branch": {
"name": "patch-2"
},
"commit": {
"type": "commit",
"hash": "f90e18fc9d45",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/commit/f90e18fc9d45"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/commits/f90e18fc9d45"
}
}
},
"repository": {
"type": "repository",
"full_name": "anbraten/test-2",
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2"
},
"avatar": {
"href": "https://bytebucket.org/ravatar/%7B26554729-595f-47d1-aedd-302625cb4a97%7D?ts=default"
}
},
"name": "test-2",
"uuid": "{26554729-595f-47d1-aedd-302625cb4a97}"
}
},
"reviewers": [],
"participants": [],
"links": {
"self": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2"
},
"html": {
"href": "https://bitbucket.org/anbraten/test-2/pull-requests/2"
},
"commits": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/commits"
},
"approve": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/approve"
},
"request-changes": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/request-changes"
},
"diff": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/diff/anbraten/test-2:f90e18fc9d45%0D006704dbeab2?from_pullrequest_id=2&topic=true"
},
"diffstat": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/diffstat/anbraten/test-2:f90e18fc9d45%0D006704dbeab2?from_pullrequest_id=2&topic=true"
},
"comments": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/comments"
},
"activity": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/activity"
},
"merge": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/merge"
},
"decline": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/decline"
},
"statuses": {
"href": "https://api.bitbucket.org/2.0/repositories/anbraten/test-2/pullrequests/2/statuses"
}
},
"summary": {
"type": "rendered",
"raw": "CHANGELOG.md created online with Bitbucket",
"markup": "markdown",
"html": "<p>CHANGELOG.md created online with Bitbucket</p>"
}
} }
} }
` `

View file

@ -25,11 +25,15 @@ import (
) )
const ( const (
hookEvent = "X-Event-Key" hookEvent = "X-Event-Key"
hookPush = "repo:push" hookPush = "repo:push"
hookPullCreated = "pullrequest:created" hookPullCreated = "pullrequest:created"
hookPullUpdated = "pullrequest:updated" hookPullUpdated = "pullrequest:updated"
stateOpen = "OPEN" hookPullMerged = "pullrequest:fulfilled"
hookPullDeclined = "pullrequest:rejected"
stateOpen = "OPEN"
stateClosed = "MERGED"
stateDeclined = "DECLINED"
) )
// parseHook parses a Bitbucket hook from an http.Request request and returns // parseHook parses a Bitbucket hook from an http.Request request and returns
@ -44,7 +48,7 @@ func parseHook(r *http.Request) (*model.Repo, *model.Pipeline, error) {
switch hookType { switch hookType {
case hookPush: case hookPush:
return parsePushHook(payload) return parsePushHook(payload)
case hookPullCreated, hookPullUpdated: case hookPullCreated, hookPullUpdated, hookPullMerged, hookPullDeclined:
return parsePullHook(payload) return parsePullHook(payload)
default: default:
return nil, nil, &types.ErrIgnoreEvent{Event: hookType} return nil, nil, &types.ErrIgnoreEvent{Event: hookType}
@ -71,15 +75,13 @@ func parsePushHook(payload []byte) (*model.Repo, *model.Pipeline, error) {
} }
// parsePullHook parses a pull request hook and returns the Repo and Pipeline // parsePullHook parses a pull request hook and returns the Repo and Pipeline
// details. If the pull request is closed nil values are returned. // details.
func parsePullHook(payload []byte) (*model.Repo, *model.Pipeline, error) { func parsePullHook(payload []byte) (*model.Repo, *model.Pipeline, error) {
hook := internal.PullRequestHook{} hook := internal.PullRequestHook{}
if err := json.Unmarshal(payload, &hook); err != nil { if err := json.Unmarshal(payload, &hook); err != nil {
return nil, nil, err return nil, nil, err
} }
if hook.PullRequest.State != stateOpen {
return nil, nil, nil
}
return convertRepo(&hook.Repo, &internal.RepoPerm{}), convertPullHook(&hook), nil return convertRepo(&hook.Repo, &internal.RepoPerm{}), convertPullHook(&hook), nil
} }

View file

@ -30,7 +30,7 @@ import (
func Test_parser(t *testing.T) { func Test_parser(t *testing.T) {
g := goblin.Goblin(t) g := goblin.Goblin(t)
g.Describe("Bitbucket parser", func() { g.Describe("Bitbucket parser", func() {
g.It("Should ignore unsupported hook", func() { g.It("should ignore unsupported hook", func() {
buf := bytes.NewBufferString(fixtures.HookPush) buf := bytes.NewBufferString(fixtures.HookPush)
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
@ -42,8 +42,8 @@ func Test_parser(t *testing.T) {
assert.ErrorIs(t, err, &types.ErrIgnoreEvent{}) assert.ErrorIs(t, err, &types.ErrIgnoreEvent{})
}) })
g.Describe("Given a pull request hook payload", func() { g.Describe("Given a pull-request hook payload", func() {
g.It("Should return err when malformed", func() { g.It("should return err when malformed", func() {
buf := bytes.NewBufferString("[]") buf := bytes.NewBufferString("[]")
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
@ -53,19 +53,7 @@ func Test_parser(t *testing.T) {
g.Assert(err).IsNotNil() g.Assert(err).IsNotNil()
}) })
g.It("Should return nil if not open", func() { g.It("should return pull-request details", func() {
buf := bytes.NewBufferString(fixtures.HookMerged)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPullCreated)
r, b, err := parseHook(req)
g.Assert(r).IsNil()
g.Assert(b).IsNil()
g.Assert(err).IsNil()
})
g.It("Should return pull request details", func() {
buf := bytes.NewBufferString(fixtures.HookPull) buf := bytes.NewBufferString(fixtures.HookPull)
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
@ -74,12 +62,39 @@ func Test_parser(t *testing.T) {
r, b, err := parseHook(req) r, b, err := parseHook(req)
g.Assert(err).IsNil() g.Assert(err).IsNil()
g.Assert(r.FullName).Equal("user_name/repo_name") g.Assert(r.FullName).Equal("user_name/repo_name")
g.Assert(b.Event).Equal(model.EventPull)
g.Assert(b.Commit).Equal("ce5965ddd289") g.Assert(b.Commit).Equal("ce5965ddd289")
}) })
g.It("should return pull-request details for a pull-request merged payload", func() {
buf := bytes.NewBufferString(fixtures.HookPullRequestMerged)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPullMerged)
r, b, err := parseHook(req)
g.Assert(err).IsNil()
g.Assert(r.FullName).Equal("anbraten/test-2")
g.Assert(b.Event).Equal(model.EventPullClosed)
g.Assert(b.Commit).Equal("6c5f0bc9b2aa")
})
g.It("should return pull-request details for a pull-request closed payload", func() {
buf := bytes.NewBufferString(fixtures.HookPullRequestDeclined)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPullDeclined)
r, b, err := parseHook(req)
g.Assert(err).IsNil()
g.Assert(r.FullName).Equal("anbraten/test-2")
g.Assert(b.Event).Equal(model.EventPullClosed)
g.Assert(b.Commit).Equal("006704dbeab2")
})
}) })
g.Describe("Given a push hook payload", func() { g.Describe("Given a push hook payload", func() {
g.It("Should return err when malformed", func() { g.It("should return err when malformed", func() {
buf := bytes.NewBufferString("[]") buf := bytes.NewBufferString("[]")
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
@ -89,7 +104,7 @@ func Test_parser(t *testing.T) {
g.Assert(err).IsNotNil() g.Assert(err).IsNotNil()
}) })
g.It("Should return nil if missing commit sha", func() { g.It("should return nil if missing commit sha", func() {
buf := bytes.NewBufferString(fixtures.HookPushEmptyHash) buf := bytes.NewBufferString(fixtures.HookPushEmptyHash)
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
@ -101,7 +116,7 @@ func Test_parser(t *testing.T) {
g.Assert(err).IsNil() g.Assert(err).IsNil()
}) })
g.It("Should return push details", func() { g.It("should return push details", func() {
buf := bytes.NewBufferString(fixtures.HookPush) buf := bytes.NewBufferString(fixtures.HookPush)
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
@ -116,5 +131,9 @@ func Test_parser(t *testing.T) {
g.Assert(b.Message).Equal("a\n") g.Assert(b.Message).Equal("a\n")
}) })
}) })
g.Describe("Given a tag hook payload", func() {
// TODO
})
}) })
} }

View file

@ -229,8 +229,8 @@ const HookPushBranch = `
} }
` `
// HookPushTag is a sample Gitea tag hook // HookTag is a sample Gitea tag hook
const HookPushTag = `{ const HookTag = `{
"sha": "ef98532add3b2feb7a137426bba1248724367df5", "sha": "ef98532add3b2feb7a137426bba1248724367df5",
"secret": "l26Un7G7HXogLAvsyf2hOA4EMARSTsR3", "secret": "l26Un7G7HXogLAvsyf2hOA4EMARSTsR3",
"ref": "v1.0.0", "ref": "v1.0.0",
@ -331,3 +331,743 @@ const HookPullRequest = `{
"avatar_url": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87" "avatar_url": "https://secure.gravatar.com/avatar/8c58a0be77ee441bb8f8595b7f1b4e87"
} }
}` }`
const HookPullRequestMerged = `
{
"action": "closed",
"number": 1,
"pull_request": {
"id": 62112,
"url": "https://gitea.com/anbraten/test-repo/pulls/1",
"number": 1,
"user": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"title": "Adjust file",
"body": "",
"labels": [],
"milestone": null,
"assignee": null,
"assignees": null,
"requested_reviewers": null,
"state": "closed",
"is_locked": false,
"comments": 1,
"html_url": "https://gitea.com/anbraten/test-repo/pulls/1",
"diff_url": "https://gitea.com/anbraten/test-repo/pulls/1.diff",
"patch_url": "https://gitea.com/anbraten/test-repo/pulls/1.patch",
"mergeable": true,
"merged": true,
"merged_at": "2023-12-05T18:35:31Z",
"merge_commit_sha": "f2440f050054df0f8ecabcace648f1683509064c",
"merged_by": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"allow_maintainer_edit": false,
"base": {
"label": "main",
"ref": "main",
"sha": "f2440f050054df0f8ecabcace648f1683509064c",
"repo_id": 46534,
"repo": {
"id": 46534,
"owner": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"name": "test-repo",
"full_name": "anbraten/test-repo",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 26,
"language": "",
"languages_url": "https://gitea.com/api/v1/repos/anbraten/test-repo/languages",
"html_url": "https://gitea.com/anbraten/test-repo",
"url": "https://gitea.com/api/v1/repos/anbraten/test-repo",
"link": "",
"ssh_url": "git@gitea.com:anbraten/test-repo.git",
"clone_url": "https://gitea.com/anbraten/test-repo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 1,
"release_counter": 0,
"default_branch": "main",
"archived": false,
"created_at": "2023-12-05T18:03:55Z",
"updated_at": "2023-12-05T18:06:29Z",
"archived_at": "1970-01-01T00:00:00Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"has_releases": true,
"has_packages": false,
"has_actions": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"allow_rebase_update": true,
"default_delete_branch_after_merge": false,
"default_merge_style": "merge",
"default_allow_maintainer_edit": false,
"avatar_url": "",
"internal": false,
"mirror_interval": "",
"mirror_updated": "0001-01-01T00:00:00Z",
"repo_transfer": null
}
},
"head": {
"label": "anbraten-patch-1",
"ref": "anbraten-patch-1",
"sha": "d555a5dd07f4d0148a58d4686ec381502ae6a2d4",
"repo_id": 46534,
"repo": {
"id": 46534,
"owner": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"name": "test-repo",
"full_name": "anbraten/test-repo",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 26,
"language": "",
"languages_url": "https://gitea.com/api/v1/repos/anbraten/test-repo/languages",
"html_url": "https://gitea.com/anbraten/test-repo",
"url": "https://gitea.com/api/v1/repos/anbraten/test-repo",
"link": "",
"ssh_url": "git@gitea.com:anbraten/test-repo.git",
"clone_url": "https://gitea.com/anbraten/test-repo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 1,
"release_counter": 0,
"default_branch": "main",
"archived": false,
"created_at": "2023-12-05T18:03:55Z",
"updated_at": "2023-12-05T18:06:29Z",
"archived_at": "1970-01-01T00:00:00Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"has_releases": true,
"has_packages": false,
"has_actions": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"allow_rebase_update": true,
"default_delete_branch_after_merge": false,
"default_merge_style": "merge",
"default_allow_maintainer_edit": false,
"avatar_url": "",
"internal": false,
"mirror_interval": "",
"mirror_updated": "0001-01-01T00:00:00Z",
"repo_transfer": null
}
},
"merge_base": "068aee163ffd44eef28a7f9ebd43e2c01774f0fa",
"due_date": null,
"created_at": "2023-12-05T18:06:38Z",
"updated_at": "2023-12-05T18:35:31Z",
"closed_at": "2023-12-05T18:35:31Z",
"pin_order": 0
},
"requested_reviewer": null,
"repository": {
"id": 46534,
"owner": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"name": "test-repo",
"full_name": "anbraten/test-repo",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 26,
"language": "",
"languages_url": "https://gitea.com/api/v1/repos/anbraten/test-repo/languages",
"html_url": "https://gitea.com/anbraten/test-repo",
"url": "https://gitea.com/api/v1/repos/anbraten/test-repo",
"link": "",
"ssh_url": "git@gitea.com:anbraten/test-repo.git",
"clone_url": "https://gitea.com/anbraten/test-repo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 1,
"release_counter": 0,
"default_branch": "main",
"archived": false,
"created_at": "2023-12-05T18:03:55Z",
"updated_at": "2023-12-05T18:06:29Z",
"archived_at": "1970-01-01T00:00:00Z",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"has_releases": true,
"has_packages": false,
"has_actions": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"allow_rebase_update": true,
"default_delete_branch_after_merge": false,
"default_merge_style": "merge",
"default_allow_maintainer_edit": false,
"avatar_url": "",
"internal": false,
"mirror_interval": "",
"mirror_updated": "0001-01-01T00:00:00Z",
"repo_transfer": null
},
"sender": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"commit_id": "",
"review": null
}
`
const HookPullRequestClosed = `
{
"action": "closed",
"number": 1,
"pull_request": {
"id": 62112,
"url": "https://gitea.com/anbraten/test-repo/pulls/1",
"number": 1,
"user": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"title": "Adjust file",
"body": "",
"labels": [],
"milestone": null,
"assignee": null,
"assignees": null,
"requested_reviewers": null,
"state": "closed",
"is_locked": false,
"comments": 0,
"html_url": "https://gitea.com/anbraten/test-repo/pulls/1",
"diff_url": "https://gitea.com/anbraten/test-repo/pulls/1.diff",
"patch_url": "https://gitea.com/anbraten/test-repo/pulls/1.patch",
"mergeable": true,
"merged": false,
"merged_at": null,
"merge_commit_sha": null,
"merged_by": null,
"allow_maintainer_edit": false,
"base": {
"label": "main",
"ref": "main",
"sha": "068aee163ffd44eef28a7f9ebd43e2c01774f0fa",
"repo_id": 46534,
"repo": {
"id": 46534,
"owner": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"name": "test-repo",
"full_name": "anbraten/test-repo",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 26,
"language": "",
"languages_url": "https://gitea.com/api/v1/repos/anbraten/test-repo/languages",
"html_url": "https://gitea.com/anbraten/test-repo",
"url": "https://gitea.com/api/v1/repos/anbraten/test-repo",
"link": "",
"ssh_url": "git@gitea.com:anbraten/test-repo.git",
"clone_url": "https://gitea.com/anbraten/test-repo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 1,
"release_counter": 0,
"default_branch": "main",
"archived": false,
"created_at": "2023-12-05T18:03:55Z",
"updated_at": "2023-12-05T18:06:29Z",
"archived_at": "1970-01-01T00:00:00Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"has_releases": true,
"has_packages": false,
"has_actions": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"allow_rebase_update": true,
"default_delete_branch_after_merge": false,
"default_merge_style": "merge",
"default_allow_maintainer_edit": false,
"avatar_url": "",
"internal": false,
"mirror_interval": "",
"mirror_updated": "0001-01-01T00:00:00Z",
"repo_transfer": null
}
},
"head": {
"label": "anbraten-patch-1",
"ref": "anbraten-patch-1",
"sha": "d555a5dd07f4d0148a58d4686ec381502ae6a2d4",
"repo_id": 46534,
"repo": {
"id": 46534,
"owner": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"name": "test-repo",
"full_name": "anbraten/test-repo",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 26,
"language": "",
"languages_url": "https://gitea.com/api/v1/repos/anbraten/test-repo/languages",
"html_url": "https://gitea.com/anbraten/test-repo",
"url": "https://gitea.com/api/v1/repos/anbraten/test-repo",
"link": "",
"ssh_url": "git@gitea.com:anbraten/test-repo.git",
"clone_url": "https://gitea.com/anbraten/test-repo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 1,
"release_counter": 0,
"default_branch": "main",
"archived": false,
"created_at": "2023-12-05T18:03:55Z",
"updated_at": "2023-12-05T18:06:29Z",
"archived_at": "1970-01-01T00:00:00Z",
"permissions": {
"admin": false,
"push": false,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"has_releases": true,
"has_packages": false,
"has_actions": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"allow_rebase_update": true,
"default_delete_branch_after_merge": false,
"default_merge_style": "merge",
"default_allow_maintainer_edit": false,
"avatar_url": "",
"internal": false,
"mirror_interval": "",
"mirror_updated": "0001-01-01T00:00:00Z",
"repo_transfer": null
}
},
"merge_base": "068aee163ffd44eef28a7f9ebd43e2c01774f0fa",
"due_date": null,
"created_at": "2023-12-05T18:06:38Z",
"updated_at": "2023-12-05T18:06:43Z",
"closed_at": "2023-12-05T18:06:43Z",
"pin_order": 0
},
"requested_reviewer": null,
"repository": {
"id": 46534,
"owner": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"name": "test-repo",
"full_name": "anbraten/test-repo",
"description": "",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 26,
"language": "",
"languages_url": "https://gitea.com/api/v1/repos/anbraten/test-repo/languages",
"html_url": "https://gitea.com/anbraten/test-repo",
"url": "https://gitea.com/api/v1/repos/anbraten/test-repo",
"link": "",
"ssh_url": "git@gitea.com:anbraten/test-repo.git",
"clone_url": "https://gitea.com/anbraten/test-repo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 1,
"release_counter": 0,
"default_branch": "main",
"archived": false,
"created_at": "2023-12-05T18:03:55Z",
"updated_at": "2023-12-05T18:06:29Z",
"archived_at": "1970-01-01T00:00:00Z",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"has_releases": true,
"has_packages": false,
"has_actions": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"allow_rebase_update": true,
"default_delete_branch_after_merge": false,
"default_merge_style": "merge",
"default_allow_maintainer_edit": false,
"avatar_url": "",
"internal": false,
"mirror_interval": "",
"mirror_updated": "0001-01-01T00:00:00Z",
"repo_transfer": null
},
"sender": {
"id": 26907,
"login": "anbraten",
"login_name": "",
"full_name": "",
"email": "anbraten@noreply.gitea.com",
"avatar_url": "https://seccdn.libravatar.org/avatar/fc9b6fe77c6b732a02925a62a81f05a0?d=identicon",
"language": "",
"is_admin": false,
"last_login": "0001-01-01T00:00:00Z",
"created": "2021-07-19T23:21:52Z",
"restricted": false,
"active": false,
"prohibit_login": false,
"location": "",
"website": "",
"description": "",
"visibility": "public",
"followers_count": 0,
"following_count": 0,
"starred_repos_count": 1,
"username": "anbraten"
},
"commit_id": "",
"review": null
}
`

View file

@ -148,8 +148,14 @@ func pipelineFromPullRequest(hook *pullRequestHook) *model.Pipeline {
hook.Repo.HTMLURL, hook.Repo.HTMLURL,
fixMalformedAvatar(hook.PullRequest.Poster.AvatarURL), fixMalformedAvatar(hook.PullRequest.Poster.AvatarURL),
) )
event := model.EventPull
if hook.Action == actionClose {
event = model.EventPullClosed
}
pipeline := &model.Pipeline{ pipeline := &model.Pipeline{
Event: model.EventPull, Event: event,
Commit: hook.PullRequest.Head.Sha, Commit: hook.PullRequest.Head.Sha,
ForgeURL: hook.PullRequest.URL, ForgeURL: hook.PullRequest.URL,
Ref: fmt.Sprintf("refs/pull/%d/head", hook.Number), Ref: fmt.Sprintf("refs/pull/%d/head", hook.Number),
@ -165,6 +171,7 @@ func pipelineFromPullRequest(hook *pullRequestHook) *model.Pipeline {
), ),
PullRequestLabels: convertLabels(hook.PullRequest.Labels), PullRequestLabels: convertLabels(hook.PullRequest.Labels),
} }
return pipeline return pipeline
} }

View file

@ -51,7 +51,7 @@ func Test_parse(t *testing.T) {
}) })
g.It("Should parse tag hook payload", func() { g.It("Should parse tag hook payload", func() {
buf := bytes.NewBufferString(fixtures.HookPushTag) buf := bytes.NewBufferString(fixtures.HookTag)
hook, err := parsePush(buf) hook, err := parsePush(buf)
g.Assert(err).IsNil() g.Assert(err).IsNil()
g.Assert(hook.Ref).Equal("v1.0.0") g.Assert(hook.Ref).Equal("v1.0.0")
@ -118,7 +118,7 @@ func Test_parse(t *testing.T) {
}) })
g.It("Should return a Pipeline struct from a tag hook", func() { g.It("Should return a Pipeline struct from a tag hook", func() {
buf := bytes.NewBufferString(fixtures.HookPushTag) buf := bytes.NewBufferString(fixtures.HookTag)
hook, _ := parsePush(buf) hook, _ := parsePush(buf)
pipeline := pipelineFromTag(hook) pipeline := pipelineFromTag(hook)
g.Assert(pipeline.Event).Equal(model.EventTag) g.Assert(pipeline.Event).Equal(model.EventTag)

View file

@ -32,10 +32,9 @@ const (
hookCreated = "create" hookCreated = "create"
hookPullRequest = "pull_request" hookPullRequest = "pull_request"
actionOpen = "opened" actionOpen = "opened"
actionSync = "synchronized" actionSync = "synchronized"
actionClose = "closed"
stateOpen = "open"
refBranch = "branch" refBranch = "branch"
refTag = "tag" refTag = "tag"
@ -53,7 +52,7 @@ func parseHook(r *http.Request) (*model.Repo, *model.Pipeline, error) {
case hookPullRequest: case hookPullRequest:
return parsePullRequestHook(r.Body) return parsePullRequestHook(r.Body)
} }
log.Debug().Msgf("unsuported hook type: '%s'", hookType) log.Debug().Msgf("unsupported hook type: '%s'", hookType)
return nil, nil, &types.ErrIgnoreEvent{Event: hookType} return nil, nil, &types.ErrIgnoreEvent{Event: hookType}
} }
@ -110,15 +109,10 @@ func parsePullRequestHook(payload io.Reader) (*model.Repo, *model.Pipeline, erro
} }
// Don't trigger pipelines for non-code changes ... // Don't trigger pipelines for non-code changes ...
if pr.Action != actionOpen && pr.Action != actionSync { if pr.Action != actionOpen && pr.Action != actionSync && pr.Action != actionClose {
log.Debug().Msgf("pull_request action is '%s' and no open or sync", pr.Action) log.Debug().Msgf("pull_request action is '%s' and no open or sync", pr.Action)
return nil, nil, nil return nil, nil, nil
} }
// ... or if PR is not open
if pr.PullRequest.State != stateOpen {
log.Debug().Msg("pull_request is closed")
return nil, nil, nil
}
repo = toRepo(pr.Repo) repo = toRepo(pr.Repo)
pipeline = pipelineFromPullRequest(pr) pipeline = pipelineFromPullRequest(pr)

View file

@ -42,18 +42,22 @@ func Test_parser(t *testing.T) {
g.Assert(b).IsNil() g.Assert(b).IsNil()
assert.ErrorIs(t, err, &types.ErrIgnoreEvent{}) assert.ErrorIs(t, err, &types.ErrIgnoreEvent{})
}) })
g.It("given a PR hook", func() {
buf := bytes.NewBufferString(fixtures.HookPullRequest) g.Describe("push event", func() {
req, _ := http.NewRequest("POST", "/hook", buf) g.It("should handle a push hook", func() {
req.Header = http.Header{} buf := bytes.NewBufferString(fixtures.HookPushBranch)
req.Header.Set(hookEvent, hookPullRequest) req, _ := http.NewRequest("POST", "/hook", buf)
r, b, err := parseHook(req) req.Header = http.Header{}
g.Assert(r).IsNotNil() req.Header.Set(hookEvent, hookPush)
g.Assert(b).IsNotNil() r, b, err := parseHook(req)
g.Assert(err).IsNil() g.Assert(err).IsNil()
g.Assert(b.Event).Equal(model.EventPull) g.Assert(r).IsNotNil()
}) g.Assert(b).IsNotNil()
g.Describe("given a push hook", func() { g.Assert(b.Event).Equal(model.EventPush)
g.Assert(b.Message).Equal("Delete '.woodpecker/.check.yml'\n")
g.Assert(b.ChangedFiles).Equal([]string{".woodpecker/.check.yml"})
})
g.It("should extract repository and pipeline details", func() { g.It("should extract repository and pipeline details", func() {
buf := bytes.NewBufferString(fixtures.HookPush) buf := bytes.NewBufferString(fixtures.HookPush)
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
@ -67,19 +71,58 @@ func Test_parser(t *testing.T) {
g.Assert(utils.EqualSliceValues(b.ChangedFiles, []string{"CHANGELOG.md", "app/controller/application.rb"})).IsTrue() g.Assert(utils.EqualSliceValues(b.ChangedFiles, []string{"CHANGELOG.md", "app/controller/application.rb"})).IsTrue()
}) })
}) })
g.Describe("given a push hook from an branch creation", func() {
g.It("should extract repository and pipeline details", func() { g.Describe("tag event", func() {
buf := bytes.NewBufferString(fixtures.HookPushBranch) g.It("should handle a tag hook", func() {
buf := bytes.NewBufferString(fixtures.HookTag)
req, _ := http.NewRequest("POST", "/hook", buf) req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{} req.Header = http.Header{}
req.Header.Set(hookEvent, hookPush) req.Header.Set(hookEvent, hookCreated)
r, b, err := parseHook(req) r, b, err := parseHook(req)
g.Assert(err).IsNil()
g.Assert(r).IsNotNil() g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil() g.Assert(b).IsNotNil()
g.Assert(b.Event).Equal(model.EventPush) g.Assert(err).IsNil()
g.Assert(b.Message).Equal("Delete '.woodpecker/.check.yml'\n") g.Assert(b.Event).Equal(model.EventTag)
g.Assert(b.ChangedFiles).Equal([]string{".woodpecker/.check.yml"}) })
})
g.Describe("pull-request events", func() {
// g.It("should handle a PR hook when PR got created")
g.It("should handle a PR hook when PR got updated", func() {
buf := bytes.NewBufferString(fixtures.HookPullRequest)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPullRequest)
r, b, err := parseHook(req)
g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil()
g.Assert(err).IsNil()
g.Assert(b.Event).Equal(model.EventPull)
})
g.It("should handle a PR closed hook when PR got closed", func() {
buf := bytes.NewBufferString(fixtures.HookPullRequestClosed)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPullRequest)
r, b, err := parseHook(req)
g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil()
g.Assert(err).IsNil()
g.Assert(b.Event).Equal(model.EventPullClosed)
})
g.It("should handle a PR closed hook when PR was merged", func() {
buf := bytes.NewBufferString(fixtures.HookPullRequestMerged)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPullRequest)
r, b, err := parseHook(req)
g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil()
g.Assert(err).IsNil()
g.Assert(b.Event).Equal(model.EventPullClosed)
}) })
}) })
}) })

File diff suppressed because it is too large Load diff

View file

@ -32,10 +32,12 @@ import (
const ( const (
hookField = "payload" hookField = "payload"
actionOpen = "opened" actionOpen = "opened"
actionSync = "synchronize" actionClose = "closed"
actionSync = "synchronize"
stateOpen = "open" stateOpen = "open"
stateClose = "closed"
) )
// parseHook parses a GitHub hook from an http.Request request and returns // parseHook parses a GitHub hook from an http.Request request and returns
@ -140,18 +142,19 @@ func parseDeployHook(hook *github.DeploymentEvent) (*model.Repo, *model.Pipeline
} }
// parsePullHook parses a pull request hook and returns the Repo and Pipeline // parsePullHook parses a pull request hook and returns the Repo and Pipeline
// details. If the pull request is closed nil values are returned. // details.
func parsePullHook(hook *github.PullRequestEvent, merge bool) (*github.PullRequest, *model.Repo, *model.Pipeline, error) { func parsePullHook(hook *github.PullRequestEvent, merge bool) (*github.PullRequest, *model.Repo, *model.Pipeline, error) {
// only listen to new merge-requests and pushes to open ones if hook.GetAction() != actionOpen && hook.GetAction() != actionSync && hook.GetAction() != actionClose {
if hook.GetAction() != actionOpen && hook.GetAction() != actionSync {
return nil, nil, nil, nil
}
if hook.GetPullRequest().GetState() != stateOpen {
return nil, nil, nil, nil return nil, nil, nil, nil
} }
event := model.EventPull
if hook.GetPullRequest().GetState() == stateClose {
event = model.EventPullClosed
}
pipeline := &model.Pipeline{ pipeline := &model.Pipeline{
Event: model.EventPull, Event: event,
Commit: hook.GetPullRequest().GetHead().GetSHA(), Commit: hook.GetPullRequest().GetHead().GetSHA(),
ForgeURL: hook.GetPullRequest().GetHTMLURL(), ForgeURL: hook.GetPullRequest().GetHTMLURL(),
Ref: fmt.Sprintf(headRefs, hook.GetPullRequest().GetNumber()), Ref: fmt.Sprintf(headRefs, hook.GetPullRequest().GetNumber()),

View file

@ -79,23 +79,7 @@ func Test_parser(t *testing.T) {
}) })
g.Describe("given a pull request hook", func() { g.Describe("given a pull request hook", func() {
g.It("should skip when action is not open or sync", func() { g.It("should handle a PR hook when PR got opened or pushed to", func() {
req := testHookRequest([]byte(fixtures.HookPullRequestInvalidAction), hookPull)
p, r, b, err := parseHook(req, false)
g.Assert(r).IsNil()
g.Assert(b).IsNil()
g.Assert(err).IsNil()
g.Assert(p).IsNil()
})
g.It("should skip when state is not open", func() {
req := testHookRequest([]byte(fixtures.HookPullRequestInvalidState), hookPull)
p, r, b, err := parseHook(req, false)
g.Assert(r).IsNil()
g.Assert(b).IsNil()
g.Assert(err).IsNil()
g.Assert(p).IsNil()
})
g.It("should extract repository and pipeline details", func() {
req := testHookRequest([]byte(fixtures.HookPullRequest), hookPull) req := testHookRequest([]byte(fixtures.HookPullRequest), hookPull)
p, r, b, err := parseHook(req, false) p, r, b, err := parseHook(req, false)
g.Assert(err).IsNil() g.Assert(err).IsNil()
@ -104,6 +88,24 @@ func Test_parser(t *testing.T) {
g.Assert(p).IsNotNil() g.Assert(p).IsNotNil()
g.Assert(b.Event).Equal(model.EventPull) g.Assert(b.Event).Equal(model.EventPull)
}) })
g.It("should handle a PR closed hook when PR got closed", func() {
req := testHookRequest([]byte(fixtures.HookPullRequestClosed), hookPull)
p, r, b, err := parseHook(req, false)
g.Assert(err).IsNil()
g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil()
g.Assert(p).IsNotNil()
g.Assert(b.Event).Equal(model.EventPullClosed)
})
g.It("should handle a PR closed hook when PR got merged", func() {
req := testHookRequest([]byte(fixtures.HookPullRequestMerged), hookPull)
p, r, b, err := parseHook(req, false)
g.Assert(err).IsNil()
g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil()
g.Assert(p).IsNotNil()
g.Assert(b.Event).Equal(model.EventPullClosed)
})
}) })
g.Describe("given a deployment hook", func() { g.Describe("given a deployment hook", func() {

View file

@ -111,6 +111,9 @@ func convertMergeRequestHook(hook *gitlab.MergeEvent, req *http.Request) (int, *
} }
pipeline.Event = model.EventPull pipeline.Event = model.EventPull
if obj.State == "closed" {
pipeline.Event = model.EventPullClosed
}
lastCommit := obj.LastCommit lastCommit := obj.LastCommit

View file

@ -137,7 +137,7 @@ func Test_GitLab(t *testing.T) {
req, _ := http.NewRequest( req, _ := http.NewRequest(
testdata.ServiceHookMethod, testdata.ServiceHookMethod,
testdata.ServiceHookURL.String(), testdata.ServiceHookURL.String(),
bytes.NewReader(testdata.ServiceHookPushBody), bytes.NewReader(testdata.HookPush),
) )
req.Header = testdata.ServiceHookHeaders req.Header = testdata.ServiceHookHeaders
@ -160,7 +160,7 @@ func Test_GitLab(t *testing.T) {
req, _ := http.NewRequest( req, _ := http.NewRequest(
testdata.ServiceHookMethod, testdata.ServiceHookMethod,
testdata.ServiceHookURL.String(), testdata.ServiceHookURL.String(),
bytes.NewReader(testdata.ServiceHookTagPushBody), bytes.NewReader(testdata.HookTag),
) )
req.Header = testdata.ServiceHookHeaders req.Header = testdata.ServiceHookHeaders
@ -182,7 +182,7 @@ func Test_GitLab(t *testing.T) {
req, _ := http.NewRequest( req, _ := http.NewRequest(
testdata.ServiceHookMethod, testdata.ServiceHookMethod,
testdata.ServiceHookURL.String(), testdata.ServiceHookURL.String(),
bytes.NewReader(testdata.WebhookMergeRequestBody), bytes.NewReader(testdata.HookPullRequest),
) )
req.Header = testdata.ServiceHookHeaders req.Header = testdata.ServiceHookHeaders
@ -198,6 +198,46 @@ func Test_GitLab(t *testing.T) {
assert.Len(t, pipeline.ChangedFiles, 0) // see L217 assert.Len(t, pipeline.ChangedFiles, 0) // see L217
} }
}) })
g.It("Should parse merge request hook when MR closed", func() {
req, _ := http.NewRequest(
testdata.ServiceHookMethod,
testdata.ServiceHookURL.String(),
bytes.NewReader(testdata.HookPullRequestClosed),
)
req.Header = testdata.ServiceHookHeaders
// TODO: insert fake store into context to retrieve user & repo, this will activate fetching of ChangedFiles
hookRepo, pipeline, err := client.Hook(ctx, req)
assert.NoError(t, err)
if assert.NotNil(t, hookRepo) && assert.NotNil(t, pipeline) {
assert.Equal(t, "main", hookRepo.Branch)
assert.Equal(t, "anbraten", hookRepo.Owner)
assert.Equal(t, "woodpecker-test", hookRepo.Name)
assert.Equal(t, "Add new file", pipeline.Title)
assert.Len(t, pipeline.ChangedFiles, 0) // see L217
}
})
g.It("Should parse merge request hook when merged", func() {
req, _ := http.NewRequest(
testdata.ServiceHookMethod,
testdata.ServiceHookURL.String(),
bytes.NewReader(testdata.HookPullRequestMerged),
)
req.Header = testdata.ServiceHookHeaders
// TODO: insert fake store into context to retrieve user & repo, this will activate fetching of ChangedFiles
hookRepo, pipeline, err := client.Hook(ctx, req)
assert.NoError(t, err)
if assert.NotNil(t, hookRepo) && assert.NotNil(t, pipeline) {
assert.Equal(t, "main", hookRepo.Branch)
assert.Equal(t, "anbraten", hookRepo.Owner)
assert.Equal(t, "woodpecker-test", hookRepo.Name)
assert.Equal(t, "Add new file", pipeline.Title)
assert.Len(t, pipeline.ChangedFiles, 0) // see L217
}
})
}) })
}) })
}) })

View file

@ -31,8 +31,9 @@ var (
} }
) )
// ServiceHookPushBody is payload of ServiceHook: Push // HookPush is payload of a push event
var ServiceHookPushBody = []byte(`{ var HookPush = []byte(`
{
"object_kind": "push", "object_kind": "push",
"event_name": "push", "event_name": "push",
"before": "ffe8eb4f91d1fe6bc49f1e610e50e4b5767f0104", "before": "ffe8eb4f91d1fe6bc49f1e610e50e4b5767f0104",
@ -100,8 +101,9 @@ var ServiceHookPushBody = []byte(`{
} }
}`) }`)
// ServiceHookTagPushBody is payload of ServiceHook: TagPush // HookTag is payload of a TAG event
var ServiceHookTagPushBody = []byte(`{ var HookTag = []byte(`
{
"object_kind": "tag_push", "object_kind": "tag_push",
"event_name": "tag_push", "event_name": "tag_push",
"before": "0000000000000000000000000000000000000000", "before": "0000000000000000000000000000000000000000",
@ -169,8 +171,9 @@ var ServiceHookTagPushBody = []byte(`{
} }
}`) }`)
// WebhookMergeRequestBody is payload of MergeEvent // HookPullRequest is payload of a PULL_REQUEST event
var WebhookMergeRequestBody = []byte(`{ var HookPullRequest = []byte(`
{
"object_kind": "merge_request", "object_kind": "merge_request",
"event_type": "merge_request", "event_type": "merge_request",
"user": { "user": {
@ -314,3 +317,285 @@ var WebhookMergeRequestBody = []byte(`{
] ]
} }
`) `)
var HookPullRequestClosed = []byte(`
{
"object_kind": "merge_request",
"event_type": "merge_request",
"user": {
"id": 2251488,
"name": "Anbraten",
"username": "anbraten",
"avatar_url": "https://secure.gravatar.com/avatar/fc9b6fe77c6b732a02925a62a81f05a0?s=80&d=identicon",
"email": "[REDACTED]"
},
"project": {
"id": 32059612,
"name": "woodpecker-test",
"description": "",
"web_url": "https://gitlab.com/anbraten/woodpecker-test",
"avatar_url": null,
"git_ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"git_http_url": "https://gitlab.com/anbraten/woodpecker-test.git",
"namespace": "Anbraten",
"visibility_level": 20,
"path_with_namespace": "anbraten/woodpecker-test",
"default_branch": "main",
"ci_config_path": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"http_url": "https://gitlab.com/anbraten/woodpecker-test.git"
},
"object_attributes": {
"assignee_id": null,
"author_id": 2251488,
"created_at": "2023-12-05 18:40:22 UTC",
"description": "",
"draft": false,
"head_pipeline_id": null,
"id": 268189426,
"iid": 4,
"last_edited_at": null,
"last_edited_by_id": null,
"merge_commit_sha": null,
"merge_error": null,
"merge_params": {
"force_remove_source_branch": "1"
},
"merge_status": "can_be_merged",
"merge_user_id": null,
"merge_when_pipeline_succeeds": false,
"milestone_id": null,
"source_branch": "patch-1",
"source_project_id": 32059612,
"state_id": 2,
"target_branch": "main",
"target_project_id": 32059612,
"time_estimate": 0,
"title": "Add new file",
"updated_at": "2023-12-05 18:40:34 UTC",
"updated_by_id": null,
"url": "https://gitlab.com/anbraten/woodpecker-test/-/merge_requests/4",
"source": {
"id": 32059612,
"name": "woodpecker-test",
"description": "",
"web_url": "https://gitlab.com/anbraten/woodpecker-test",
"avatar_url": null,
"git_ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"git_http_url": "https://gitlab.com/anbraten/woodpecker-test.git",
"namespace": "Anbraten",
"visibility_level": 20,
"path_with_namespace": "anbraten/woodpecker-test",
"default_branch": "main",
"ci_config_path": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"http_url": "https://gitlab.com/anbraten/woodpecker-test.git"
},
"target": {
"id": 32059612,
"name": "woodpecker-test",
"description": "",
"web_url": "https://gitlab.com/anbraten/woodpecker-test",
"avatar_url": null,
"git_ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"git_http_url": "https://gitlab.com/anbraten/woodpecker-test.git",
"namespace": "Anbraten",
"visibility_level": 20,
"path_with_namespace": "anbraten/woodpecker-test",
"default_branch": "main",
"ci_config_path": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"http_url": "https://gitlab.com/anbraten/woodpecker-test.git"
},
"last_commit": {
"id": "3e4db3586b65dd401de8c77b3ac343fd24cbf89b",
"message": "Add new file",
"title": "Add new file",
"timestamp": "2023-12-05T18:39:57+00:00",
"url": "https://gitlab.com/anbraten/woodpecker-test/-/commit/3e4db3586b65dd401de8c77b3ac343fd24cbf89b",
"author": {
"name": "Anbraten",
"email": "[redacted]"
}
},
"work_in_progress": false,
"total_time_spent": 0,
"time_change": 0,
"human_total_time_spent": null,
"human_time_change": null,
"human_time_estimate": null,
"assignee_ids": [],
"reviewer_ids": [],
"labels": [],
"state": "closed",
"blocking_discussions_resolved": true,
"first_contribution": false,
"detailed_merge_status": "not_open",
"action": "close"
},
"labels": [],
"changes": {
"state_id": {
"previous": 1,
"current": 2
},
"updated_at": {
"previous": "2023-12-05 18:40:28 UTC",
"current": "2023-12-05 18:40:34 UTC"
}
},
"repository": {
"name": "woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"description": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test"
}
}
`)
var HookPullRequestMerged = []byte(`
{
"object_kind": "merge_request",
"event_type": "merge_request",
"user": {
"id": 2251488,
"name": "Anbraten",
"username": "anbraten",
"avatar_url": "https://secure.gravatar.com/avatar/fc9b6fe77c6b732a02925a62a81f05a0?s=80&d=identicon",
"email": "[REDACTED]"
},
"project": {
"id": 32059612,
"name": "woodpecker-test",
"description": "",
"web_url": "https://gitlab.com/anbraten/woodpecker-test",
"avatar_url": null,
"git_ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"git_http_url": "https://gitlab.com/anbraten/woodpecker-test.git",
"namespace": "Anbraten",
"visibility_level": 20,
"path_with_namespace": "anbraten/woodpecker-test",
"default_branch": "main",
"ci_config_path": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"http_url": "https://gitlab.com/anbraten/woodpecker-test.git"
},
"object_attributes": {
"assignee_id": null,
"author_id": 2251488,
"created_at": "2023-12-05 18:40:22 UTC",
"description": "",
"draft": false,
"head_pipeline_id": null,
"id": 268189426,
"iid": 4,
"last_edited_at": null,
"last_edited_by_id": null,
"merge_commit_sha": "43411b53d670203e887c4985c4e58e8e6b7c109e",
"merge_error": null,
"merge_params": {
"force_remove_source_branch": "1"
},
"merge_status": "can_be_merged",
"merge_user_id": null,
"merge_when_pipeline_succeeds": false,
"milestone_id": null,
"source_branch": "patch-1",
"source_project_id": 32059612,
"state_id": 3,
"target_branch": "main",
"target_project_id": 32059612,
"time_estimate": 0,
"title": "Add new file",
"updated_at": "2023-12-05 18:43:00 UTC",
"updated_by_id": null,
"url": "https://gitlab.com/anbraten/woodpecker-test/-/merge_requests/4",
"source": {
"id": 32059612,
"name": "woodpecker-test",
"description": "",
"web_url": "https://gitlab.com/anbraten/woodpecker-test",
"avatar_url": null,
"git_ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"git_http_url": "https://gitlab.com/anbraten/woodpecker-test.git",
"namespace": "Anbraten",
"visibility_level": 20,
"path_with_namespace": "anbraten/woodpecker-test",
"default_branch": "main",
"ci_config_path": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"http_url": "https://gitlab.com/anbraten/woodpecker-test.git"
},
"target": {
"id": 32059612,
"name": "woodpecker-test",
"description": "",
"web_url": "https://gitlab.com/anbraten/woodpecker-test",
"avatar_url": null,
"git_ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"git_http_url": "https://gitlab.com/anbraten/woodpecker-test.git",
"namespace": "Anbraten",
"visibility_level": 20,
"path_with_namespace": "anbraten/woodpecker-test",
"default_branch": "main",
"ci_config_path": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"ssh_url": "git@gitlab.com:anbraten/woodpecker-test.git",
"http_url": "https://gitlab.com/anbraten/woodpecker-test.git"
},
"last_commit": {
"id": "3e4db3586b65dd401de8c77b3ac343fd24cbf89b",
"message": "Add new file",
"title": "Add new file",
"timestamp": "2023-12-05T18:39:57+00:00",
"url": "https://gitlab.com/anbraten/woodpecker-test/-/commit/3e4db3586b65dd401de8c77b3ac343fd24cbf89b",
"author": {
"name": "Anbraten",
"email": "[redacted]"
}
},
"work_in_progress": false,
"total_time_spent": 0,
"time_change": 0,
"human_total_time_spent": null,
"human_time_change": null,
"human_time_estimate": null,
"assignee_ids": [],
"reviewer_ids": [],
"labels": [],
"state": "merged",
"blocking_discussions_resolved": true,
"first_contribution": false,
"detailed_merge_status": "not_open",
"action": "merge"
},
"labels": [],
"changes": {
"state_id": {
"previous": 4,
"current": 3
},
"updated_at": {
"previous": "2023-12-05 18:43:00 UTC",
"current": "2023-12-05 18:43:00 UTC"
}
},
"repository": {
"name": "woodpecker-test",
"url": "git@gitlab.com:anbraten/woodpecker-test.git",
"description": "",
"homepage": "https://gitlab.com/anbraten/woodpecker-test"
}
}
`)

View file

@ -23,12 +23,13 @@ import (
type WebhookEvent string // @name WebhookEvent type WebhookEvent string // @name WebhookEvent
const ( const (
EventPush WebhookEvent = "push" EventPush WebhookEvent = "push"
EventPull WebhookEvent = "pull_request" EventPull WebhookEvent = "pull_request"
EventTag WebhookEvent = "tag" EventPullClosed WebhookEvent = "pull_request_closed"
EventDeploy WebhookEvent = "deployment" EventTag WebhookEvent = "tag"
EventCron WebhookEvent = "cron" EventDeploy WebhookEvent = "deployment"
EventManual WebhookEvent = "manual" EventCron WebhookEvent = "cron"
EventManual WebhookEvent = "manual"
) )
type WebhookEventList []WebhookEvent type WebhookEventList []WebhookEvent

View file

@ -2,7 +2,8 @@
<i-ic-sharp-timelapse v-if="name === 'duration'" class="h-6 w-6" /> <i-ic-sharp-timelapse v-if="name === 'duration'" class="h-6 w-6" />
<i-mdi-clock-time-eight-outline v-else-if="name === 'since'" class="h-6 w-6" /> <i-mdi-clock-time-eight-outline v-else-if="name === 'since'" class="h-6 w-6" />
<i-mdi-source-branch v-else-if="name === 'push'" class="h-6 w-6" /> <i-mdi-source-branch v-else-if="name === 'push'" class="h-6 w-6" />
<i-mdi-source-pull v-else-if="name === 'pull_request'" class="h-6 w-6" /> <i-mdi-source-pull v-else-if="name === 'pull-request'" class="h-6 w-6" />
<i-mdi-source-merge v-else-if="name === 'pull-request-closed'" class="h-6 w-6" />
<i-mdi-gesture-tap v-else-if="name === 'manual-pipeline'" class="h-6 w-6" /> <i-mdi-gesture-tap v-else-if="name === 'manual-pipeline'" class="h-6 w-6" />
<i-mdi-tag-outline v-else-if="name === 'tag'" class="h-6 w-6" /> <i-mdi-tag-outline v-else-if="name === 'tag'" class="h-6 w-6" />
<i-clarity-deploy-line v-else-if="name === 'deployment'" class="h-6 w-6" /> <i-clarity-deploy-line v-else-if="name === 'deployment'" class="h-6 w-6" />
@ -58,7 +59,8 @@ export type IconNames =
| 'duration' | 'duration'
| 'since' | 'since'
| 'push' | 'push'
| 'pull_request' | 'pull-request'
| 'pull-request-closed'
| 'manual-pipeline' | 'manual-pipeline'
| 'tag' | 'tag'
| 'deployment' | 'deployment'

View file

@ -37,7 +37,8 @@
class="grid grid-rows-2 grid-flow-col w-full md:ml-auto md:w-96 py-2 gap-x-4 gap-y-2 flex-shrink-0 text-wp-text-100" class="grid grid-rows-2 grid-flow-col w-full md:ml-auto md:w-96 py-2 gap-x-4 gap-y-2 flex-shrink-0 text-wp-text-100"
> >
<div class="flex space-x-2 items-center min-w-0"> <div class="flex space-x-2 items-center min-w-0">
<Icon v-if="pipeline.event === 'pull_request'" name="pull_request" /> <Icon v-if="pipeline.event === 'pull_request'" name="pull-request" />
<Icon v-if="pipeline.event === 'pull_request_closed'" name="pull-request-closed" />
<Icon v-else-if="pipeline.event === 'deployment'" name="deployment" /> <Icon v-else-if="pipeline.event === 'deployment'" name="deployment" />
<Icon v-else-if="pipeline.event === 'tag'" name="tag" /> <Icon v-else-if="pipeline.event === 'tag'" name="tag" />
<Icon v-else-if="pipeline.event === 'cron'" name="push" /> <Icon v-else-if="pipeline.event === 'cron'" name="push" />

View file

@ -11,11 +11,11 @@
<span>{{ pipeline.author }}</span> <span>{{ pipeline.author }}</span>
</div> </div>
<a <a
v-if="pipeline.event === 'pull_request'" v-if="pipeline.event === 'pull_request' || pipeline.event === 'pull_request_closed'"
class="flex items-center space-x-1 text-wp-link-100 hover:text-wp-link-200 min-w-0" class="flex items-center space-x-1 text-wp-link-100 hover:text-wp-link-200 min-w-0"
:href="pipeline.forge_url" :href="pipeline.forge_url"
> >
<Icon name="pull_request" /> <Icon name="pull-request" />
<span class="truncate">{{ prettyRef }}</span> <span class="truncate">{{ prettyRef }}</span>
</a> </a>
<router-link <router-link

View file

@ -96,7 +96,7 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
return pipeline.value.ref.replaceAll('refs/tags/', ''); return pipeline.value.ref.replaceAll('refs/tags/', '');
} }
if (pipeline.value?.event === 'pull_request') { if (pipeline.value?.event === 'pull_request' || pipeline.value?.event === 'pull_request_closed') {
return `#${pipeline.value.ref return `#${pipeline.value.ref
.replaceAll('refs/pull/', '') .replaceAll('refs/pull/', '')
.replaceAll('refs/merge-requests/', '') .replaceAll('refs/merge-requests/', '')

View file

@ -2,6 +2,7 @@ export enum WebhookEvents {
Push = 'push', Push = 'push',
Tag = 'tag', Tag = 'tag',
PullRequest = 'pull_request', PullRequest = 'pull_request',
PullRequestClosed = 'pull_request_closed',
Deploy = 'deployment', Deploy = 'deployment',
Cron = 'cron', Cron = 'cron',
Manual = 'manual', Manual = 'manual',

View file

@ -24,6 +24,9 @@ if (!repo || !repoPermissions) {
const allPipelines = inject<Ref<Pipeline[]>>('pipelines'); const allPipelines = inject<Ref<Pipeline[]>>('pipelines');
const pipelines = computed( const pipelines = computed(
() => allPipelines?.value.filter((b) => b.branch === branch.value && b.event !== 'pull_request'), () =>
allPipelines?.value.filter(
(b) => b.branch === branch.value && b.event !== 'pull_request' && b.event !== 'pull_request_closed',
),
); );
</script> </script>

View file

@ -29,7 +29,7 @@ const pipelines = computed(
() => () =>
allPipelines?.value.filter( allPipelines?.value.filter(
(b) => (b) =>
b.event === 'pull_request' && (b.event === 'pull_request' || b.event === 'pull_request_closed') &&
b.ref b.ref
.replaceAll('refs/pull/', '') .replaceAll('refs/pull/', '')
.replaceAll('refs/merge-requests/', '') .replaceAll('refs/merge-requests/', '')

View file

@ -88,7 +88,7 @@
<Tab id="config" :title="$t('repo.pipeline.config')" /> <Tab id="config" :title="$t('repo.pipeline.config')" />
<Tab <Tab
v-if=" v-if="
(pipeline.event === 'push' || pipeline.event === 'pull_request') && (pipeline.event === 'push' || pipeline.event === 'pull_request' || pipeline.event === 'pull_request_closed') &&
pipeline.changed_files && pipeline.changed_files &&
pipeline.changed_files.length > 0 pipeline.changed_files.length > 0
" "

View file

@ -16,10 +16,13 @@ package woodpecker
// Event values. // Event values.
const ( const (
EventPush = "push" EventPush = "push"
EventPull = "pull_request" EventPull = "pull_request"
EventTag = "tag" EventPullClosed = "pull_request_closed"
EventDeploy = "deployment" EventTag = "tag"
EventDeploy = "deployment"
EventCron = "cron"
EventManual = "manual"
) )
// Status values. // Status values.