Skip to content

Commit 4368552

Browse files
authored
Merge pull request #18 from saitho/feature/gitea-support
Gitea support
2 parents 3e09c97 + 2ab1ab4 commit 4368552

File tree

4 files changed

+245
-6
lines changed

4 files changed

+245
-6
lines changed

README.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22

33
[![Build Status](https://travis-ci.org/tf/redmine_merge_request_links.svg?branch=master)](https://travis-ci.org/tf/redmine_merge_request_links)
44

5-
Display links to associated GitLab merge requests and GitHub pull
6-
requests on Redmine's issue page.
5+
Display links to associated merge requests and pull requests on Redmine's issue page.
76

8-
Intercepts webhooks and parses merge request descriptions for
9-
mentioned issue ids.
7+
Intercepts webhooks and parses merge request descriptions for mentioned issue ids.
8+
9+
The following platforms are supported:
10+
11+
* GitHub
12+
* GitLab
13+
* Gitea
1014

1115

1216
## Requirements
1317

1418
* Redmine 3 (tested with 3.4.6)
1519

16-
1720
## Installation
1821

1922
Copy plugin directoy to `{RAILS_APP}/plugins` on your Redmine
@@ -82,6 +85,24 @@ Create a webhook in GitLab or GitHub as described here:
8285

8386
* Click "Add webhook".
8487

88+
### Gitea
89+
90+
* Go to the webhook page of a project or organization.
91+
92+
* Enter the URL of your Redmine instance
93+
`https://redmine.example.com/merge_requests/event`.
94+
95+
* Select `application/json` as content type.
96+
97+
* Enter the secret token you defined in environment variable
98+
`REDMINE_MERGE_REQUEST_LINKS_GITEA_WEBHOOK_TOKEN`.
99+
100+
* Choose "Custom events...".
101+
102+
* Check the "Pull requests" event.
103+
104+
* Click "Add webhook".
105+
85106
### Redmine
86107

87108
To display associated merge requests on issue pages:
@@ -101,7 +122,7 @@ on the issue's Redmine page.
101122

102123
## Known Issues
103124

104-
* Gitlab only passes the author id as part of the merge request
125+
* GitLab only passes the author id as part of the merge request
105126
webhook not a display name. It does include the username of the user
106127
whose action triggered the webhook, though. To prevent having to
107128
fetch the author name in a separate REST API call, this username is

lib/redmine_merge_request_links.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
module RedmineMergeRequestLinks
44
github_token = ENV['REDMINE_MERGE_REQUEST_LINKS_GITHUB_WEBHOOK_TOKEN']
55
gitlab_token = ENV['REDMINE_MERGE_REQUEST_LINKS_GITLAB_WEBHOOK_TOKEN']
6+
gitea_token = ENV['REDMINE_MERGE_REQUEST_LINKS_GITEA_WEBHOOK_TOKEN']
67

78
mattr_accessor :event_handlers
89
self.event_handlers = [
10+
RedmineMergeRequestLinks::EventHandlers::Gitea.new(token: gitea_token),
911
RedmineMergeRequestLinks::EventHandlers::Github.new(token: github_token),
1012
RedmineMergeRequestLinks::EventHandlers::Gitlab.new(token: gitlab_token)
1113
]
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
module RedmineMergeRequestLinks
2+
module EventHandlers
3+
class Gitea
4+
def initialize(token:)
5+
@token = token
6+
end
7+
8+
def matches?(request)
9+
request.headers['X-Gitea-Event'] == 'pull_request'
10+
end
11+
12+
def verify(request)
13+
request.body.rewind
14+
payload = request.body.read
15+
16+
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
17+
@token,
18+
payload)
19+
20+
Rack::Utils.secure_compare(signature,
21+
request.headers['X-Gitea-Signature'])
22+
end
23+
24+
def parse_params(params)
25+
params
26+
.require(:pull_request)
27+
.permit(:state, :merged, :html_url, :title, :body, :number,
28+
user: :login,
29+
base: { repo: :full_name }).tap do |attributes|
30+
31+
merged = attributes.delete(:merged)
32+
user = attributes.delete(:user) || {}
33+
base = attributes.delete(:base) || {}
34+
repo = base.fetch(:repo, {})
35+
36+
if attributes[:state] == 'closed' && merged
37+
attributes[:state] = 'merged'
38+
end
39+
40+
attributes[:provider] = 'gitea'
41+
attributes[:url] = attributes.delete(:html_url)
42+
attributes[:description] = attributes.delete(:body)
43+
attributes[:author_name] = "@#{user[:login]}"
44+
45+
attributes[:display_id] =
46+
"#{repo[:full_name]}##{attributes.delete(:number)}"
47+
end
48+
end
49+
end
50+
end
51+
end

test/functional/merge_requests_controller_test.rb

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class MergeRequestsControllerTest < ActionController::TestCase
1010

1111
def setup
1212
RedmineMergeRequestLinks.event_handlers = [
13+
RedmineMergeRequestLinks::EventHandlers::Gitea.new(token: TOKEN),
1314
RedmineMergeRequestLinks::EventHandlers::Github.new(token: TOKEN),
1415
RedmineMergeRequestLinks::EventHandlers::Gitlab.new(token: TOKEN)
1516
]
@@ -348,6 +349,164 @@ def test_sets_state_to_merged_if_closed_and_merged
348349
assert_equal 'merged', merge_request.state
349350
end
350351

352+
def test_gitea_pull_request_event_creates_merge_request
353+
url = 'https://gitea.com/Codertocat/Hello-World/pull/1'
354+
355+
payload = {
356+
pull_request: {
357+
html_url: url,
358+
title: 'Some pull request',
359+
state: 'closed',
360+
number: 12,
361+
user: {
362+
login: 'someuser'
363+
},
364+
base: {
365+
repo: {
366+
full_name: 'group/project'
367+
}
368+
}
369+
}
370+
}
371+
request.headers['X-Gitea-Event'] = 'pull_request'
372+
request.headers['X-GitHub-Event'] = 'pull_request'
373+
request.headers['X-Gogs-Event'] = 'pull_request'
374+
request.headers['X-Gitea-Signature'] = gitea_signature(payload)
375+
request.headers['X-Gogs-Signature'] = gitea_signature(payload)
376+
377+
post(:event, payload)
378+
379+
assert_response :success
380+
381+
merge_request = MergeRequest.where(url: url).first
382+
assert merge_request.present?
383+
assert_equal 'closed', merge_request.state
384+
assert_equal 'Some pull request', merge_request.title
385+
assert_equal 'group/project#12', merge_request.display_id
386+
assert_equal '@someuser', merge_request.author_name
387+
assert_equal 'gitea', merge_request.provider
388+
end
389+
390+
def test_responds_with_forbidden_if_gitea_signature_is_incorrect
391+
request.headers['X-Gitea-Event'] = 'pull_request'
392+
request.headers['X-Gitea-Signature'] = 'wrong'
393+
request.headers['X-Gogs-Event'] = 'pull_request'
394+
request.headers['X-Gogs-Signature'] = 'wrong'
395+
post(:event, pull_request: {
396+
html_url: 'https://gitea.com/Codertocat/Hello-World/pull/1',
397+
title: 'Some pull request',
398+
state: 'closed',
399+
number: 12,
400+
user: {
401+
login: 'someuser'
402+
},
403+
base: {
404+
repo: {
405+
full_name: 'group/project'
406+
}
407+
}
408+
})
409+
410+
assert_response :forbidden
411+
end
412+
413+
def test_associates_issues_mentioned_in_gitea_pr_body
414+
url = 'https://gitea.com/Codertocat/Hello-World/pull/1'
415+
issue = Issue.last
416+
417+
payload = {
418+
pull_request: {
419+
html_url: url,
420+
title: 'Some pull request',
421+
state: 'closed',
422+
body: "Talks about ##{issue.id}",
423+
number: 12,
424+
user: {
425+
login: 'someuser'
426+
},
427+
base: {
428+
repo: {
429+
full_name: 'group/project'
430+
}
431+
}
432+
}
433+
}
434+
request.headers['X-Gitea-Event'] = 'pull_request'
435+
request.headers['X-GitHub-Event'] = 'pull_request'
436+
request.headers['X-Gogs-Event'] = 'pull_request'
437+
request.headers['X-Gitea-Signature'] = gitea_signature(payload)
438+
request.headers['X-Gogs-Signature'] = gitea_signature(payload)
439+
post(:event, payload)
440+
441+
merge_request = MergeRequest.where(url: url).first
442+
assert_includes(merge_request.issues, issue)
443+
end
444+
445+
def test_associates_issues_mentioned_in_gitea_pr_title
446+
url = 'https://gitea.com/Codertocat/Hello-World/pull/1'
447+
issue = Issue.last
448+
449+
payload = {
450+
pull_request: {
451+
html_url: url,
452+
title: "Some pull request (##{issue.id})",
453+
state: 'closed',
454+
body: 'Some text',
455+
number: 12,
456+
user: {
457+
login: 'someuser'
458+
},
459+
base: {
460+
repo: {
461+
full_name: 'group/project'
462+
}
463+
}
464+
}
465+
}
466+
request.headers['X-Gitea-Event'] = 'pull_request'
467+
request.headers['X-GitHub-Event'] = 'pull_request'
468+
request.headers['X-Gogs-Event'] = 'pull_request'
469+
request.headers['X-Gitea-Signature'] = gitea_signature(payload)
470+
request.headers['X-Gogs-Signature'] = gitea_signature(payload)
471+
post(:event, payload)
472+
473+
merge_request = MergeRequest.where(url: url).first
474+
assert_includes(merge_request.issues, issue)
475+
end
476+
477+
def test_sets_state_to_merged_if_gitea_pr_is_closed_and_merged
478+
url = 'https://gitea.com/Codertocat/Hello-World/pull/1'
479+
480+
payload = {
481+
pull_request: {
482+
html_url: url,
483+
title: 'Some pull request',
484+
state: 'closed',
485+
merged: true,
486+
number: 12,
487+
user: {
488+
login: 'someuser'
489+
},
490+
base: {
491+
repo: {
492+
full_name: 'group/project'
493+
}
494+
}
495+
}
496+
}
497+
request.headers['X-Gitea-Event'] = 'pull_request'
498+
request.headers['X-GitHub-Event'] = 'pull_request'
499+
request.headers['X-Gogs-Event'] = 'pull_request'
500+
request.headers['X-Gitea-Signature'] = gitea_signature(payload)
501+
request.headers['X-Gogs-Signature'] = gitea_signature(payload)
502+
post(:event, payload)
503+
504+
assert_response :success
505+
506+
merge_request = MergeRequest.where(url: url).first
507+
assert_equal 'merged', merge_request.state
508+
end
509+
351510
def test_responds_with_bad_request_if_unknown_event
352511
post(:event)
353512

@@ -361,4 +520,10 @@ def hub_signature(payload)
361520
TOKEN,
362521
payload.to_query)
363522
end
523+
524+
def gitea_signature(payload)
525+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'),
526+
TOKEN,
527+
payload.to_query)
528+
end
364529
end

0 commit comments

Comments
 (0)