gitlab-mail-receiver 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ff6d380ad5fe4c2cd3ff4066cfa4c371907a20c
4
- data.tar.gz: f3018919650a3ac10e1bc1bbe30a4810430edee8
3
+ metadata.gz: 685533be718d29db08f1ba52b4b3118a166bc7d8
4
+ data.tar.gz: 85d33b4ce3c0ddbaaf1118ba95a7a82e211e6210
5
5
  SHA512:
6
- metadata.gz: 45eaca00bbd532de05c158f898cf3b9e2cfb05d84ad9069b2e18ef661f44b04c884e4d52e02030b9d90356033f751639f765f937e92e59b49bbf554bd3767b93
7
- data.tar.gz: 3c186bb6c06e452f6b1c3d2929f145d3053da9ab311ce1440af0460a7e9c8e7644ffe9743a31a5fba1f693438a8030cf441307cb6a6614ad9dd8bf4bb61d9f04
6
+ metadata.gz: 29173d1720d26e9a9ca378e67c5b6fff0825b51e223093533b56eb56f5298602246ef9f3ecf6d336d9706c787ec17fab90275736ce05d678283e969ce4050390
7
+ data.tar.gz: a4583f133dd241987ec5cd66b108c567a306c433321d3e027843a4ac497a5e71f4e2bb2e312662ca7b4afe7fb6fde549a7b6ac7beca9774c38610c96a092135e
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # GitLab Mail Receiver
2
2
 
3
- This gem can allow your GitLab to receive emails to create Issue comments like GitHub.
3
+ [![Gem Version](https://badge.fury.io/rb/gitlab-mail-receiver.svg)](http://badge.fury.io/rb/gitlab-mail-receiver) [![CI Status](https://travis-ci.org/huacnlee/gitlab-mail-receiver.svg)](https://travis-ci.org/huacnlee/gitlab-mail-receiver)
4
+
5
+ The way of allow your GitLab support Email receive and parse the email content, and find Issue/MergeRequest to create reply.
4
6
 
5
7
  [中文介绍](https://ruby-china.org/topics/27143)
6
8
 
@@ -44,27 +46,52 @@ MailReceiver.configure do
44
46
  self.sender = 'xxx@your-mail-host.com'
45
47
  self.poll_interval = 5
46
48
  self.imap = {
47
- server: 'imap.your-mail-host.com'
48
- port: 993
49
- ssl: true
50
- username: 'xxx@your-mail-host.com'
49
+ server: 'imap.your-mail-host.com',
50
+ port: 993,
51
+ ssl: true,
52
+ username: 'xxx@your-mail-host.com',
51
53
  password: 'your-password'
52
54
  }
53
55
  end
54
56
  ```
55
57
 
56
- ## Run
58
+ ## Run commands
57
59
 
58
60
  ```
59
61
  $ cd gitlab
60
- $ bundle exec gitlab-mail-receiver
62
+ $ bundle exec gitlab-mail-receiver -h
63
+ Commands:
64
+ gitlab-mail-receiver help [COMMAND] # Describe available commands or one specific command
65
+ gitlab-mail-receiver restart # Restart Daemon
66
+ gitlab-mail-receiver start # Start Daemon
67
+ gitlab-mail-receiver stop # Stop Daemon
68
+ gitlab-mail-receiver version # Show version
69
+
70
+ Options:
71
+ [--root=ROOT]
72
+ # Default: ./
73
+ $ bundle exec gitlab-mail-receiver start
74
+ Started gitlab-mail-receiver on pid: 59386
75
+ I, [2015-09-01T13:36:50.813124 #59387] INFO -- : Celluloid 0.17.1.2 is running in BACKPORTED mode. [ http://git.io/vJf3J ]
76
+ ...
61
77
  ```
62
78
 
63
- > NOTE: The daemon log will write to `$rails_root/log/gitlab-mail-receiver.log`
64
-
65
79
  ## Run in production
66
80
 
67
81
  ```
68
82
  $ cd gitlab
69
- $ RAILS_ENV=production nohup bundle exec gitlab-mail-receiver &
83
+ $ RAILS_ENV=production bundle exec gitlab-mail-receiver start -d
84
+ pid_file: ./tmp/pids/gitlab-mail-receiver.pid
85
+ log_file: ./log/gitlab-mail-receiver.log
86
+ Started gitlab-mail-receiver on pid: 58861
87
+ ```
88
+
89
+ > NOTE: The daemon log will write to `$rails_root/log/gitlab-mail-receiver.log`
90
+
91
+ Stop daemon
92
+
93
+ ```bash
94
+ $ bundle exec gitlab-mail-receiver stop
95
+ Stoping gitlab-mail-receiver... [OK]
96
+ ```
70
97
  ```
@@ -1,26 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'bundler/setup'
4
- require_relative '../lib/gitlab-mail-receiver'
2
+ require_relative '../lib/mail-receiver/cli'
5
3
 
6
- app_root = ENV['GITLAB_ROOT'] || "."
7
-
8
- begin
9
- rails_env = ::File.expand_path('./config/environment', app_root)
10
- require rails_env
11
- rescue => e
12
- puts "You need run this command under GitLab root."
13
- return
14
- end
15
-
16
- logger = Logger.new(File.join(app_root, 'log/gitlab-mail-receiver.log'))
17
- Mailman.config.logger = logger
18
- Mailman.config.rails_root = app_root
19
-
20
- logger.info "Starting Mailman ..."
21
- Mailman::Application.run do
22
- to '%user%+%suffix%@%host%' do
23
- @receiver = MailReceiver::Receiver.new(message, logger: logger)
24
- @receiver.process!
25
- end
26
- end
4
+ MailReceiver::CLI.start(ARGV)
@@ -1,6 +1,8 @@
1
+ require_relative './mail-receiver/encoder'
1
2
  require_relative './mail-receiver/body_parser'
2
3
  require_relative './mail-receiver/receiver'
3
4
  require_relative './mail-receiver/reply_to'
5
+
4
6
  require "mailman"
5
7
  require 'active_support/core_ext'
6
8
 
@@ -15,6 +17,10 @@ end
15
17
  Mailman::Configuration.send(:include, MailmanConfig)
16
18
 
17
19
  module MailReceiver
20
+ def self.config
21
+ Mailman.config
22
+ end
23
+
18
24
  def self.configure(&block)
19
25
  Mailman.config.instance_exec(&block)
20
26
  end
@@ -0,0 +1,55 @@
1
+ require_relative './daemon'
2
+ require 'thor'
3
+
4
+ module MailReceiver
5
+ class CLI < Thor
6
+ include Thor::Actions
7
+
8
+ map '-v' => :version
9
+ map "s" => :start
10
+ class_option :root, type: :string, default: './'
11
+
12
+ option :daemon, type: :boolean, aliases: ['d'], default: false
13
+ desc "start", "Start Daemon"
14
+ def start
15
+ MailReceiver::Daemon.init(options) do
16
+ begin
17
+ rails_env = ::File.expand_path('./config/environment', options[:root])
18
+ require rails_env
19
+ rescue => e
20
+ puts "You need run this command under GitLab root."
21
+ return
22
+ end
23
+
24
+ Mailman.config.logger = Logger.new($stdout)
25
+ Mailman.config.rails_root = options[:root]
26
+
27
+ Mailman.config.logger.info "Starting gitlab-mail-receiver..."
28
+ Mailman::Application.run do
29
+ to '%user%+%suffix%@%host%' do
30
+ @receiver = MailReceiver::Receiver.new(message, logger: Mailman.config.logger)
31
+ @receiver.process!
32
+ end
33
+ end
34
+ end
35
+ MailReceiver::Daemon.start_process
36
+ end
37
+
38
+ desc "stop", "Stop Daemon"
39
+ def stop
40
+ MailReceiver::Daemon.init(options)
41
+ MailReceiver::Daemon.stop_process
42
+ end
43
+
44
+ desc "restart", "Restart Daemon"
45
+ def restart
46
+ MailReceiver::Daemon.init(options)
47
+ MailReceiver::Daemon.restart_process
48
+ end
49
+
50
+ desc "version", "Show version"
51
+ def version
52
+ puts "gitlab-mail-receiver #{MailReceiver.version}"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,162 @@
1
+ # Daemon code from: https://github.com/huacnlee/sails
2
+ module MailReceiver
3
+ class Daemon
4
+ class << self
5
+ attr_accessor :options, :daemon, :mode, :app_name, :pid_file, :log_file, :runblock
6
+
7
+ def init(opts = {}, &block)
8
+ self.app_name = 'gitlab-mail-receiver'
9
+ self.pid_file = File.join(opts[:root], "tmp/pids/gitlab-mail-receiver.pid")
10
+ self.log_file = File.join(opts[:root], 'log/gitlab-mail-receiver.log')
11
+ self.daemon = opts[:daemon]
12
+ self.options = opts
13
+ self.runblock = block
14
+ end
15
+
16
+ def read_pid
17
+ if !File.exist?(pid_file)
18
+ return nil
19
+ end
20
+
21
+ pid = File.open(pid_file).read.to_i
22
+ begin
23
+ Process.getpgid(pid)
24
+ rescue
25
+ pid = nil
26
+ end
27
+ pid
28
+ end
29
+
30
+ def start_process
31
+ old_pid = read_pid
32
+ if !old_pid.nil?
33
+ puts colorize("Current have #{app_name} process in running on pid #{old_pid}", :red)
34
+ return
35
+ end
36
+
37
+ # start master process
38
+ @master_pid = fork_master_process!
39
+ File.open(pid_file, "w+") do |f|
40
+ f.puts @master_pid
41
+ end
42
+
43
+ if self.daemon
44
+ puts "pid_file: #{self.pid_file}"
45
+ puts "log_file: #{self.log_file}"
46
+ end
47
+ puts "Started #{app_name} on pid: #{@master_pid}"
48
+ # puts "in init: #{Sails.service.object_id}"
49
+
50
+ if not self.daemon
51
+ Process.waitpid(@master_pid)
52
+ else
53
+ exit
54
+ end
55
+ end
56
+
57
+ def restart_process(options = {})
58
+ old_pid = read_pid
59
+ if old_pid == nil
60
+ puts colorize("#{app_name} process not found on pid #{old_pid}", :red)
61
+ return
62
+ end
63
+
64
+ print "Restarting #{app_name}..."
65
+ Process.kill("USR2", old_pid)
66
+ puts colorize(" [OK]", :green)
67
+ end
68
+
69
+ def fork_master_process!
70
+ fork do
71
+ # WARN: DO NOT CALL Sails IN THIS BLOCK!
72
+ $PROGRAM_NAME = self.app_name + " [master]"
73
+ @child_pid = fork_child_process!
74
+
75
+ Signal.trap("QUIT") do
76
+ Process.kill("QUIT", @child_pid)
77
+ exit
78
+ end
79
+
80
+ Signal.trap("USR2") do
81
+ Process.kill("USR2", @child_pid)
82
+ end
83
+
84
+ loop do
85
+ sleep 1
86
+ begin
87
+ Process.getpgid(@child_pid)
88
+ rescue Errno::ESRCH
89
+ @child_pid = fork_child_process!
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def fork_child_process!
96
+ pid = fork do
97
+ $PROGRAM_NAME = self.app_name + " [worker]"
98
+ Signal.trap("QUIT") do
99
+ exit
100
+ end
101
+
102
+ Signal.trap("USR2") do
103
+ # TODO: reload Sails in current process
104
+ exit
105
+ end
106
+
107
+ if self.daemon == true
108
+ redirect_stdout
109
+ end
110
+
111
+ # puts "in child: #{Sails.service.object_id}"
112
+ self.runblock.call
113
+ end
114
+ # http://ruby-doc.org/core-1.9.3/Process.html#detach-method
115
+ Process.detach(pid)
116
+ pid
117
+ end
118
+
119
+ def stop_process
120
+ pid = read_pid
121
+ if pid.nil?
122
+ puts colorize("#{app_name} process not found, pid #{pid}", :red)
123
+ return
124
+ end
125
+
126
+ print "Stoping #{app_name}..."
127
+ begin
128
+ Process.kill("QUIT", pid)
129
+ ensure
130
+ File.delete(pid_file)
131
+ end
132
+ puts colorize(" [OK]", :green)
133
+ end
134
+
135
+ private
136
+ # Redirect stdout, stderr to log file,
137
+ # If we not do this, stdout will block sails daemon, for example `puts`.
138
+ def redirect_stdout
139
+ redirect_io($stdout, self.log_file)
140
+ redirect_io($stderr, self.log_file)
141
+ end
142
+
143
+ def redirect_io(io, path)
144
+ File.open(path, 'ab') { |fp| io.reopen(fp) } if path
145
+ io.sync = true
146
+ end
147
+
148
+ def colorize(text, c)
149
+ case c
150
+ when :red
151
+ return ["\033[31m",text,"\033[0m"].join("")
152
+ when :green
153
+ return ["\033[32m",text,"\033[0m"].join("")
154
+ when :blue
155
+ return ["\033[34m",text,"\033[0m"].join("")
156
+ else
157
+ return text
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_support/core_ext/hash'
2
+ require 'rack'
3
+
4
+ module MailReceiver
5
+ module Encoder
6
+ class << self
7
+ def encode(hash)
8
+ hash.to_query
9
+ end
10
+
11
+ def decode(query)
12
+ Rack::Utils.parse_query(query).deep_symbolize_keys
13
+ end
14
+ end
15
+ end
16
+ end
@@ -12,15 +12,19 @@ module MailReceiver
12
12
  end
13
13
 
14
14
  def project_slug
15
- @project_slug ||= id_prefix.split(/!|#/).first
15
+ hash_data[:p]
16
16
  end
17
17
 
18
18
  def issue_id
19
- @issue_id ||= id_prefix.split(/!|#/).last
19
+ hash_data[:id]
20
+ end
21
+
22
+ def target_id
23
+ hash_data[:n]
20
24
  end
21
25
 
22
26
  def merge_request?
23
- @merge_request ||= id_prefix.match('!')
27
+ @merge_request ||= hash_data[:t].downcase == 'm'
24
28
  end
25
29
 
26
30
  def body
@@ -42,14 +46,23 @@ module MailReceiver
42
46
  @prefix ||= to.split('@').first
43
47
  end
44
48
 
49
+ # foo+p=chair/chair&id=123 => { p: chair/chair, id: 123 }
50
+ def hash_data
51
+ return @hash_data if defined?(@hash_data)
52
+ return {} if not prefix.include?('+')
53
+ @hash_data = Encoder.decode(prefix.split('+').last)
54
+ return @hash_data
55
+ end
56
+
45
57
  def inspect
46
- { project_slug: project_slug, issue_id: issue_id, merge_request: merge_request?, to: to, body: body}
58
+ { project_slug: project_slug, issue_id: issue_id, target_id: target_id, merge_request: merge_request?, to: to, body: body}
47
59
  end
48
60
 
49
61
  def project
50
62
  @project ||= Project.find_with_namespace(project_slug)
51
63
  rescue => e
52
64
  logger.warn "Project: #{project_slug} record not found."
65
+ nil
53
66
  end
54
67
 
55
68
  def process!
@@ -67,6 +80,16 @@ module MailReceiver
67
80
  return if note_params.blank?
68
81
 
69
82
  note_params[:project_id] = project.id
83
+
84
+ # relation to target Note
85
+ if target_id
86
+ target_note = project.notes.find_by_id(target_id)
87
+ if target_note
88
+ note_params[:commit_id] = target_note.commit_id
89
+ note_params[:line_code] = target_note.line_code
90
+ end
91
+ end
92
+
70
93
  note_params[:note] = body
71
94
 
72
95
  @note = Notes::CreateService.new(project, current_user, note_params).execute
@@ -99,17 +122,8 @@ module MailReceiver
99
122
  @current_user ||= User.find_by_any_email(from)
100
123
  end
101
124
 
102
- private
103
-
104
125
  def logger
105
126
  @logger ||= Logger.new($stdout)
106
127
  end
107
-
108
- # foo+chair/chair!123 => chair/chair!123
109
- def id_prefix
110
- return @id_prefix if defined?(@id_prefix)
111
- @id_prefix = prefix.split('+').last
112
- return @id_prefix
113
- end
114
128
  end
115
129
  end
@@ -1,11 +1,9 @@
1
1
  require 'mailman'
2
+ require 'json'
2
3
 
3
4
  module MailReceiver
4
5
  module ReplyTo
5
6
  def mail_new_thread(model, headers = {}, &block)
6
- headers['Message-ID'] = message_id(model)
7
- headers['X-GitLab-Project'] = "#{@project.name} | " if @project
8
-
9
7
  # Mail receiver
10
8
  headers[:reply_to] = reply_to_address(model)
11
9
 
@@ -13,10 +11,6 @@ module MailReceiver
13
11
  end
14
12
 
15
13
  def mail_answer_thread(model, headers = {}, &block)
16
- headers['In-Reply-To'] = message_id(model)
17
- headers['References'] = message_id(model)
18
- headers['X-GitLab-Project'] = "#{@project.name} | " if @project
19
-
20
14
  if headers[:subject]
21
15
  headers[:subject].prepend('Re: ')
22
16
  end
@@ -29,39 +23,37 @@ module MailReceiver
29
23
 
30
24
  protected
31
25
  def reply_to_address(model)
32
- able_path = convert_able_path(model)
33
- return default_email_reply_to if able_path.blank?
26
+ hash = convert_able(model)
27
+ return default_email_reply_to if hash.blank?
34
28
  return default_email_reply_to if @project.blank?
35
29
 
36
- slug = "#{@project.path_with_namespace}#{able_path}"
37
30
 
38
- Mailman.config.sender.sub('@', "+#{slug}@")
39
- end
31
+ hash.merge!({ p: @project.path_with_namespace })
40
32
 
33
+ suffix = Encoder.encode(hash)
41
34
 
35
+ Mailman.config.sender.sub('@', "+#{suffix}@")
36
+ end
42
37
 
43
38
  def default_email_reply_to
44
39
  Gitlab.config.gitlab.email_reply_to
45
40
  end
46
41
 
47
- def convert_able_path(model)
48
- if model.class.name == 'Issue'
49
- return "##{model.iid}"
42
+ def convert_able(model)
43
+ res = { id: model.iid }
44
+ if defined?(@note)
45
+ # gitlab/app/mailers/emails/notes.rb 里面会声明 @note
46
+ res.merge!({ n: @note.id })
50
47
  end
51
48
 
52
- if model.class.name == 'MergeRequest'
53
- return "!#{model.iid}"
49
+ if model.class.name == 'Issue'
50
+ res.merge({ t: 'i' })
51
+ return res
54
52
  end
55
53
 
56
-
57
- if model.class.name == 'Note'
58
- if model.noteable_type == 'Issue' && model.noteable
59
- return "##{model.noteable.iid}"
60
- end
61
-
62
- if model.noteable_type == 'MergeRequest' && model.noteable
63
- return "!#{model.noteable.iid}"
64
- end
54
+ if model.class.name == 'MergeRequest'
55
+ res.merge({ t: 't' })
56
+ return res
65
57
  end
66
58
 
67
59
  return nil
@@ -0,0 +1,7 @@
1
+ module MailReceiver
2
+ class << self
3
+ def version
4
+ '0.0.2'
5
+ end
6
+ end
7
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-mail-receiver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-31 00:00:00.000000000 Z
11
+ date: 2015-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mailman
@@ -38,7 +38,36 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '4.0'
41
- description: Allow your GitLab to receive mails to create Issue comment.
41
+ - !ruby/object:Gem::Dependency
42
+ name: rack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 0.17.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 0.17.0
69
+ description: The way of allow your GitLab support Email receive and parse the email
70
+ content, and find Issue/MergeRequest to create reply.
42
71
  email:
43
72
  - huacnlee@gmail.com
44
73
  executables:
@@ -50,10 +79,13 @@ files:
50
79
  - README.md
51
80
  - bin/gitlab-mail-receiver
52
81
  - lib/gitlab-mail-receiver.rb
53
- - lib/gitlab-mail-receiver/railtie.rb
54
82
  - lib/mail-receiver/body_parser.rb
83
+ - lib/mail-receiver/cli.rb
84
+ - lib/mail-receiver/daemon.rb
85
+ - lib/mail-receiver/encoder.rb
55
86
  - lib/mail-receiver/receiver.rb
56
87
  - lib/mail-receiver/reply_to.rb
88
+ - lib/mail-receiver/version.rb
57
89
  homepage: http://github.com/huacnlee/gitlab-mail-receiver
58
90
  licenses:
59
91
  - MIT
@@ -77,6 +109,6 @@ rubyforge_project:
77
109
  rubygems_version: 2.4.7
78
110
  signing_key:
79
111
  specification_version: 4
80
- summary: Allow your GitLab to receive mails to create Issue comment
112
+ summary: Allow your GitLab receive mails to create Issue comment
81
113
  test_files: []
82
114
  has_rdoc:
@@ -1,7 +0,0 @@
1
- module GitLabMailReceiver
2
- module Rails
3
- class Railtie < ::Rails::Railtie
4
-
5
- end
6
- end
7
- end