Commit f8ad4d2b422af14d57c2c8d49134ef0ef60a1695

Authored by miks
2 parents 723104c4 78d88262

Merge branch 'master' into deploy_keys

Conflicts:
	app/models/project.rb
  1 +before_install: sudo apt-get install libicu-dev -y
1 branches: 2 branches:
2 only: 3 only:
3 - 'master' 4 - 'master'
@@ -22,7 +22,9 @@ gem "acts_as_list" @@ -22,7 +22,9 @@ gem "acts_as_list"
22 gem "rdiscount" 22 gem "rdiscount"
23 gem "acts-as-taggable-on", "~> 2.1.0" 23 gem "acts-as-taggable-on", "~> 2.1.0"
24 gem "drapper" 24 gem "drapper"
25 -gem "rchardet19", "~> 1.3.5" 25 +gem "resque"
  26 +gem "httparty"
  27 +gem "charlock_holmes"
26 28
27 group :assets do 29 group :assets do
28 gem "sass-rails", "~> 3.1.0" 30 gem "sass-rails", "~> 3.1.0"
@@ -47,6 +49,7 @@ group :development, :test do @@ -47,6 +49,7 @@ group :development, :test do
47 gem "awesome_print" 49 gem "awesome_print"
48 gem "database_cleaner" 50 gem "database_cleaner"
49 gem "launchy" 51 gem "launchy"
  52 + gem "webmock"
50 end 53 end
51 54
52 group :test do 55 group :test do
@@ -77,6 +77,7 @@ GEM @@ -77,6 +77,7 @@ GEM
77 xpath (~> 0.1.4) 77 xpath (~> 0.1.4)
78 carrierwave (0.5.8) 78 carrierwave (0.5.8)
79 activesupport (~> 3.0) 79 activesupport (~> 3.0)
  80 + charlock_holmes (0.6.8)
80 childprocess (0.2.2) 81 childprocess (0.2.2)
81 ffi (~> 1.0.6) 82 ffi (~> 1.0.6)
82 coffee-rails (3.1.1) 83 coffee-rails (3.1.1)
@@ -87,6 +88,7 @@ GEM @@ -87,6 +88,7 @@ GEM
87 execjs 88 execjs
88 coffee-script-source (1.1.3) 89 coffee-script-source (1.1.3)
89 columnize (0.3.4) 90 columnize (0.3.4)
  91 + crack (0.3.1)
90 daemons (1.1.4) 92 daemons (1.1.4)
91 database_cleaner (0.7.0) 93 database_cleaner (0.7.0)
92 devise (1.5.0) 94 devise (1.5.0)
@@ -111,6 +113,9 @@ GEM @@ -111,6 +113,9 @@ GEM
111 railties (~> 3.0) 113 railties (~> 3.0)
112 hashery (1.4.0) 114 hashery (1.4.0)
113 hike (1.2.1) 115 hike (1.2.1)
  116 + httparty (0.8.1)
  117 + multi_json
  118 + multi_xml
114 i18n (0.6.0) 119 i18n (0.6.0)
115 jquery-rails (1.0.17) 120 jquery-rails (1.0.17)
116 railties (~> 3.0) 121 railties (~> 3.0)
@@ -132,6 +137,7 @@ GEM @@ -132,6 +137,7 @@ GEM
132 treetop (~> 1.4.8) 137 treetop (~> 1.4.8)
133 mime-types (1.17.2) 138 mime-types (1.17.2)
134 multi_json (1.0.3) 139 multi_json (1.0.3)
  140 + multi_xml (0.4.1)
135 nokogiri (1.5.0) 141 nokogiri (1.5.0)
136 orm_adapter (0.0.5) 142 orm_adapter (0.0.5)
137 polyglot (0.3.3) 143 polyglot (0.3.3)
@@ -143,6 +149,8 @@ GEM @@ -143,6 +149,8 @@ GEM
143 rack (>= 0.4) 149 rack (>= 0.4)
144 rack-mount (0.8.3) 150 rack-mount (0.8.3)
145 rack (>= 1.0.0) 151 rack (>= 1.0.0)
  152 + rack-protection (1.1.4)
  153 + rack
146 rack-ssl (1.3.2) 154 rack-ssl (1.3.2)
147 rack 155 rack
148 rack-test (0.6.1) 156 rack-test (0.6.1)
@@ -165,10 +173,17 @@ GEM @@ -165,10 +173,17 @@ GEM
165 rdoc (~> 3.4) 173 rdoc (~> 3.4)
166 thor (~> 0.14.6) 174 thor (~> 0.14.6)
167 rake (0.9.2.2) 175 rake (0.9.2.2)
168 - rchardet19 (1.3.5)  
169 rdiscount (1.6.8) 176 rdiscount (1.6.8)
170 rdoc (3.11) 177 rdoc (3.11)
171 json (~> 1.4) 178 json (~> 1.4)
  179 + redis (2.2.2)
  180 + redis-namespace (1.0.3)
  181 + redis (< 3.0.0)
  182 + resque (1.19.0)
  183 + multi_json (~> 1.0)
  184 + redis-namespace (~> 1.0.2)
  185 + sinatra (>= 0.9.2)
  186 + vegas (~> 0.1.2)
172 rspec (2.7.0) 187 rspec (2.7.0)
173 rspec-core (~> 2.7.0) 188 rspec-core (~> 2.7.0)
174 rspec-expectations (~> 2.7.0) 189 rspec-expectations (~> 2.7.0)
@@ -220,6 +235,10 @@ GEM @@ -220,6 +235,10 @@ GEM
220 multi_json (~> 1.0.3) 235 multi_json (~> 1.0.3)
221 simplecov-html (~> 0.5.3) 236 simplecov-html (~> 0.5.3)
222 simplecov-html (0.5.3) 237 simplecov-html (0.5.3)
  238 + sinatra (1.3.1)
  239 + rack (~> 1.3, >= 1.3.4)
  240 + rack-protection (~> 1.1, >= 1.1.2)
  241 + tilt (~> 1.3, >= 1.3.3)
223 six (0.2.0) 242 six (0.2.0)
224 sprockets (2.0.3) 243 sprockets (2.0.3)
225 hike (~> 1.2) 244 hike (~> 1.2)
@@ -244,8 +263,13 @@ GEM @@ -244,8 +263,13 @@ GEM
244 uglifier (1.1.0) 263 uglifier (1.1.0)
245 execjs (>= 0.3.0) 264 execjs (>= 0.3.0)
246 multi_json (>= 1.0.2) 265 multi_json (>= 1.0.2)
  266 + vegas (0.1.8)
  267 + rack (>= 1.0.0)
247 warden (1.1.0) 268 warden (1.1.0)
248 rack (>= 1.0) 269 rack (>= 1.0)
  270 + webmock (1.7.8)
  271 + addressable (~> 2.2, > 2.2.5)
  272 + crack (>= 0.1.7)
249 xpath (0.1.4) 273 xpath (0.1.4)
250 nokogiri (~> 1.3) 274 nokogiri (~> 1.3)
251 275
@@ -261,6 +285,7 @@ DEPENDENCIES @@ -261,6 +285,7 @@ DEPENDENCIES
261 awesome_print 285 awesome_print
262 capybara 286 capybara
263 carrierwave 287 carrierwave
  288 + charlock_holmes
264 coffee-rails (~> 3.1.0) 289 coffee-rails (~> 3.1.0)
265 database_cleaner 290 database_cleaner
266 devise (= 1.5.0) 291 devise (= 1.5.0)
@@ -270,6 +295,7 @@ DEPENDENCIES @@ -270,6 +295,7 @@ DEPENDENCIES
270 gitolite! 295 gitolite!
271 grit! 296 grit!
272 haml-rails 297 haml-rails
  298 + httparty
273 jquery-rails 299 jquery-rails
274 kaminari 300 kaminari
275 launchy 301 launchy
@@ -277,8 +303,8 @@ DEPENDENCIES @@ -277,8 +303,8 @@ DEPENDENCIES
277 pygments.rb (= 0.2.3) 303 pygments.rb (= 0.2.3)
278 rails (= 3.1.1) 304 rails (= 3.1.1)
279 rails-footnotes (~> 3.7.5) 305 rails-footnotes (~> 3.7.5)
280 - rchardet19 (~> 1.3.5)  
281 rdiscount 306 rdiscount
  307 + resque
282 rspec-rails 308 rspec-rails
283 ruby-debug19 309 ruby-debug19
284 sass-rails (~> 3.1.0) 310 sass-rails (~> 3.1.0)
@@ -292,3 +318,4 @@ DEPENDENCIES @@ -292,3 +318,4 @@ DEPENDENCIES
292 thin 318 thin
293 turn 319 turn
294 uglifier 320 uglifier
  321 + webmock
@@ -5,9 +5,9 @@ GitLab is a free Project/Repository management application @@ -5,9 +5,9 @@ GitLab is a free Project/Repository management application
5 5
6 ## Application details 6 ## Application details
7 7
8 -rails 3.1  
9 -works only with gitolite  
10 -sqlite as default a database 8 +* rails 3.1
  9 +* works only with gitolite
  10 +* sqlite as default a database
11 11
12 ## Requirements 12 ## Requirements
13 13
app/assets/images/favicon.png

338 Bytes

app/assets/stylesheets/notes.css.scss
@@ -42,3 +42,11 @@ body.project-page #notes-list .note span.note-author strong{font-weight: bold; f @@ -42,3 +42,11 @@ body.project-page #notes-list .note span.note-author strong{font-weight: bold; f
42 42
43 43
44 .note .note-title { margin-left:55px; } 44 .note .note-title { margin-left:55px; }
  45 +
  46 +p.notify_controls input{
  47 + margin: 5px;
  48 +}
  49 +
  50 +p.notify_controls span{
  51 + font-weight: 700;
  52 +}
app/assets/stylesheets/style.scss
@@ -359,6 +359,9 @@ header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 294px; margin @@ -359,6 +359,9 @@ header nav{border-radius: 4px; box-shadow: 0 1px 2px black; width: 294px; margin
359 margin-top: 2px; 359 margin-top: 2px;
360 height:30px 360 height:30px
361 } 361 }
  362 +header nav.shorter_nav{
  363 + width: 207px;
  364 +}
362 header nav a{padding: 8px 12px 8px 34px; display: inline-block; color: #D6DADF; border-right: 1px solid #31363E; position: relative; box-shadow: 1px 0 0 rgba(255,255,255,.1); margin: 0} 365 header nav a{padding: 8px 12px 8px 34px; display: inline-block; color: #D6DADF; border-right: 1px solid #31363E; position: relative; box-shadow: 1px 0 0 rgba(255,255,255,.1); margin: 0}
363 header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;} 366 header nav a span{width: 20px; height: 20px; display: inline-block; background: red; position: absolute; left: 8px; top: 6px;}
364 header nav a:last-child {border: 0; box-shadow: none} 367 header nav a:last-child {border: 0; box-shadow: none}
@@ -382,7 +385,7 @@ header nav a.dashboard { @@ -382,7 +385,7 @@ header nav a.dashboard {
382 border-bottom-left-radius: 4px; 385 border-bottom-left-radius: 4px;
383 } 386 }
384 387
385 -header nav a.admin{ 388 +header nav a.last_elem{
386 -webkit-border-top-right-radius: 4px; 389 -webkit-border-top-right-radius: 4px;
387 -webkit-border-bottom-right-radius: 4px; 390 -webkit-border-bottom-right-radius: 4px;
388 -moz-border-radius-topright: 4px; 391 -moz-border-radius-topright: 4px;
app/helpers/commits_helper.rb
@@ -23,4 +23,23 @@ module CommitsHelper @@ -23,4 +23,23 @@ module CommitsHelper
23 link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit), 23 link_to "More", project_commits_path(@project, :offset => offset.to_i + limit.to_i, :limit => limit),
24 :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link" 24 :remote => true, :class => "lite_button vm", :style => "text-align:center; width:930px; ", :id => "more-commits-link"
25 end 25 end
  26 +
  27 + def commit_msg_with_link_to_issues(project, message)
  28 + return '' unless message
  29 + out = ''
  30 + message.split(/(#[0-9]+)/m).each do |m|
  31 + if m =~ /(#([0-9]+))/m
  32 + begin
  33 + issue = project.issues.find($2)
  34 + out += link_to($1, project_issue_path(project, $2))
  35 + rescue
  36 + out += $1
  37 + end
  38 + else
  39 + out += m
  40 + end
  41 + end
  42 + preserve out
  43 + end
  44 +
26 end 45 end
app/models/project.rb
@@ -15,6 +15,7 @@ class Project &lt; ActiveRecord::Base @@ -15,6 +15,7 @@ class Project &lt; ActiveRecord::Base
15 has_many :notes, :dependent => :destroy 15 has_many :notes, :dependent => :destroy
16 has_many :snippets, :dependent => :destroy 16 has_many :snippets, :dependent => :destroy
17 has_many :deploy_keys, :dependent => :destroy 17 has_many :deploy_keys, :dependent => :destroy
  18 + has_many :web_hooks, :dependent => :destroy
18 19
19 acts_as_taggable 20 acts_as_taggable
20 21
@@ -83,12 +84,58 @@ class Project &lt; ActiveRecord::Base @@ -83,12 +84,58 @@ class Project &lt; ActiveRecord::Base
83 :heads, 84 :heads,
84 :commits_since, 85 :commits_since,
85 :fresh_commits, 86 :fresh_commits,
  87 + :commits_between,
86 :to => :repository, :prefix => nil 88 :to => :repository, :prefix => nil
87 89
88 def to_param 90 def to_param
89 code 91 code
90 end 92 end
91 93
  94 + def web_url
  95 + [GIT_HOST['host'], code].join("/")
  96 + end
  97 +
  98 + def execute_web_hooks(oldrev, newrev, ref)
  99 + ref_parts = ref.split('/')
  100 +
  101 + # Return if this is not a push to a branch (e.g. new commits)
  102 + return if ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000"
  103 +
  104 + data = web_hook_data(oldrev, newrev, ref)
  105 + web_hooks.each { |web_hook| web_hook.execute(data) }
  106 + end
  107 +
  108 + def web_hook_data(oldrev, newrev, ref)
  109 + data = {
  110 + before: oldrev,
  111 + after: newrev,
  112 + ref: ref,
  113 + repository: {
  114 + name: name,
  115 + url: web_url,
  116 + description: description,
  117 + homepage: web_url,
  118 + private: private?
  119 + },
  120 + commits: []
  121 + }
  122 +
  123 + commits_between(oldrev, newrev).each do |commit|
  124 + data[:commits] << {
  125 + id: commit.id,
  126 + message: commit.safe_message,
  127 + timestamp: commit.date.xmlschema,
  128 + url: "http://#{GIT_HOST['host']}/#{code}/commits/#{commit.id}",
  129 + author: {
  130 + name: commit.author_name,
  131 + email: commit.author_email
  132 + }
  133 + }
  134 + end
  135 +
  136 + data
  137 + end
  138 +
92 def team_member_by_name_or_email(email = nil, name = nil) 139 def team_member_by_name_or_email(email = nil, name = nil)
93 user = users.where("email like ? or name like ?", email, name).first 140 user = users.where("email like ? or name like ?", email, name).first
94 users_projects.find_by_user_id(user.id) if user 141 users_projects.find_by_user_id(user.id) if user
app/models/repository.rb
@@ -31,6 +31,22 @@ class Repository @@ -31,6 +31,22 @@ class Repository
31 project.id 31 project.id
32 end 32 end
33 33
  34 + def write_hooks
  35 + %w(post-receive).each do |hook|
  36 + write_hook(hook, File.read(File.join(Rails.root, 'lib', "#{hook}-hook")))
  37 + end
  38 + end
  39 +
  40 + def write_hook(name, content)
  41 + hook_file = File.join(project.path_to_repo, 'hooks', name)
  42 +
  43 + File.open(hook_file, 'w') do |f|
  44 + f.write(content)
  45 + end
  46 +
  47 + File.chmod(0775, hook_file)
  48 + end
  49 +
34 def repo 50 def repo
35 @repo ||= Grit::Repo.new(project.path_to_repo) 51 @repo ||= Grit::Repo.new(project.path_to_repo)
36 end 52 end
@@ -47,6 +63,8 @@ class Repository @@ -47,6 +63,8 @@ class Repository
47 Gitlabhq::GitHost.system.new.configure do |c| 63 Gitlabhq::GitHost.system.new.configure do |c|
48 c.update_project(path, project) 64 c.update_project(path, project)
49 end 65 end
  66 +
  67 + write_hooks if File.exists?(project.path_to_repo)
50 end 68 end
51 69
52 def destroy_repository 70 def destroy_repository
@@ -115,4 +133,8 @@ class Repository @@ -115,4 +133,8 @@ class Repository
115 repo.commits(ref) 133 repo.commits(ref)
116 end.map{ |c| Commit.new(c) } 134 end.map{ |c| Commit.new(c) }
117 end 135 end
  136 +
  137 + def commits_between(from, to)
  138 + repo.commits_between(from, to).map { |c| Commit.new(c) }
  139 + end
118 end 140 end
app/models/web_hook.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +class WebHook < ActiveRecord::Base
  2 + include HTTParty
  3 +
  4 + # HTTParty timeout
  5 + default_timeout 10
  6 +
  7 + belongs_to :project
  8 +
  9 + validates :url,
  10 + presence: true,
  11 + format: {
  12 + with: URI::regexp(%w(http https)),
  13 + message: "should be a valid url" }
  14 +
  15 + def execute(data)
  16 + WebHook.post(url, body: data.to_json)
  17 + rescue
  18 + # There was a problem calling this web hook, let's forget about it.
  19 + end
  20 +end
app/views/commits/show.html.haml
@@ -18,8 +18,7 @@ @@ -18,8 +18,7 @@
18 18
19 %hr 19 %hr
20 %pre.commit_message 20 %pre.commit_message
21 - = preserve @commit.safe_message  
22 - 21 + = commit_msg_with_link_to_issues(@project, @commit.safe_message)
23 .clear 22 .clear
24 %br 23 %br
25 24
app/views/layouts/_head_panel.html.erb
@@ -21,16 +21,18 @@ @@ -21,16 +21,18 @@
21 <%= text_field_tag "search", nil, :placeholder => "Search", :class => "search-input" %> 21 <%= text_field_tag "search", nil, :placeholder => "Search", :class => "search-input" %>
22 </div> 22 </div>
23 <!-- .login-top --> 23 <!-- .login-top -->
24 - <nav>  
25 - <%= link_to dashboard_path, :class => current_page?(root_path) ? "current dashboard" : "dashboard" do %> 24 + <nav class="<%= 'shorter_nav' unless current_user.is_admin? %>">
  25 + <%= link_to dashboard_path, :class => "#{'current' if current_page?(root_path)} dashboard" do %>
26 <span></span>Dashboard 26 <span></span>Dashboard
27 <% end %> 27 <% end %>
28 - <%= link_to projects_path, :class => current_page?(projects_path) ? "current project" : "project" do %> 28 + <%= link_to projects_path, :class =>"#{'current' if current_page?(projects_path)} project #{'last_elem' unless current_user.is_admin?}" do %>
29 <span></span>Projects 29 <span></span>Projects
30 <% end %> 30 <% end %>
31 - <%= link_to((current_user.is_admin? ? admin_root_path : "#"), :class => (admin_namespace? ? "current admin" : "admin")) do %>  
32 - <span></span>Admin  
33 - <% end %> 31 + <% if current_user.is_admin? %>
  32 + <%= link_to((current_user.is_admin? ? admin_root_path : "#"), :class => "#{'current' if admin_namespace?} admin last_elem") do %>
  33 + <span></span>Admin
  34 + <% end %>
  35 + <% end %>
34 </nav> 36 </nav>
35 37
36 </header> 38 </header>
app/views/layouts/admin.html.haml
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 %head 3 %head
4 %title 4 %title
5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?} 5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?}
  6 + = favicon_link_tag 'favicon.ico'
6 = stylesheet_link_tag "application" 7 = stylesheet_link_tag "application"
7 = javascript_include_tag "application" 8 = javascript_include_tag "application"
8 = csrf_meta_tags 9 = csrf_meta_tags
app/views/layouts/devise.html.haml
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 %head 3 %head
4 %title 4 %title
5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?} 5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?}
  6 + = favicon_link_tag 'favicon.ico'
6 = stylesheet_link_tag "application" 7 = stylesheet_link_tag "application"
7 = javascript_include_tag "application" 8 = javascript_include_tag "application"
8 = csrf_meta_tags 9 = csrf_meta_tags
app/views/layouts/profile.html.haml
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 %head 3 %head
4 %title 4 %title
5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?} 5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?}
  6 + = favicon_link_tag 'favicon.ico'
6 = stylesheet_link_tag "application" 7 = stylesheet_link_tag "application"
7 = javascript_include_tag "application" 8 = javascript_include_tag "application"
8 = csrf_meta_tags 9 = csrf_meta_tags
app/views/layouts/project.html.haml
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 %head 3 %head
4 %title 4 %title
5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?} 5 GitLab #{" - #{@project.name}" if @project && !@project.new_record?}
  6 + = favicon_link_tag 'favicon.ico'
6 = stylesheet_link_tag "application" 7 = stylesheet_link_tag "application"
7 = javascript_include_tag "application" 8 = javascript_include_tag "application"
8 - if current_page?(tree_project_ref_path(@project, @project.root_ref)) || current_page?(project_commits_path(@project)) 9 - if current_page?(tree_project_ref_path(@project, @project.root_ref)) || current_page?(project_commits_path(@project))
app/views/notes/_form.html.haml
@@ -22,13 +22,15 @@ @@ -22,13 +22,15 @@
22 %br 22 %br
23 %br 23 %br
24 = f.file_field :attachment 24 = f.file_field :attachment
  25 +
  26 + %p.notify_controls
  27 + %span Notify:
  28 + = check_box_tag :notify, 1, @note.noteable_type != "Commit"
  29 + = label_tag :notify, "Project team"
25 30
26 - = check_box_tag :notify, 1, @note.noteable_type != "Commit"  
27 - = label_tag :notify, "Notify project team about your note"  
28 -  
29 - -if @note.noteable_type == "Commit"  
30 - = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"  
31 - = label_tag :notify_author, "Notify commit author about your note" 31 + -if @note.noteable_type == "Commit"
  32 + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
  33 + = label_tag :notify_author, "Commit author"
32 34
33 .clear 35 .clear
34 %br 36 %br
app/workers/post_receive.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +class PostReceive
  2 + def self.perform(reponame, oldrev, newrev, ref)
  3 + project = Project.find_by_path(reponame)
  4 + return false if project.nil?
  5 +
  6 + project.execute_web_hooks(oldrev, newrev, ref)
  7 + end
  8 +end
config/routes.rb
1 Gitlab::Application.routes.draw do 1 Gitlab::Application.routes.draw do
2 2
  3 + # Optionally, enable Resque here
  4 + # require 'resque/server'
  5 + # mount Resque::Server.new, at: '/info/resque'
3 6
4 get 'tags'=> 'tags#index' 7 get 'tags'=> 'tags#index'
5 get 'tags/:tag' => 'projects#index' 8 get 'tags/:tag' => 'projects#index'
db/migrate/20111214091851_create_web_hooks.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class CreateWebHooks < ActiveRecord::Migration
  2 + def change
  3 + create_table :web_hooks do |t|
  4 + t.string :url
  5 + t.integer :project_id
  6 + t.timestamps
  7 + end
  8 + end
  9 +end
@@ -13,18 +13,6 @@ @@ -13,18 +13,6 @@
13 13
14 ActiveRecord::Schema.define(:version => 20111220190817) do 14 ActiveRecord::Schema.define(:version => 20111220190817) do
15 15
16 - create_table "features", :force => true do |t|  
17 - t.string "name"  
18 - t.string "branch_name"  
19 - t.integer "assignee_id"  
20 - t.integer "author_id"  
21 - t.integer "project_id"  
22 - t.datetime "created_at"  
23 - t.datetime "updated_at"  
24 - t.string "version"  
25 - t.integer "status", :default => 0, :null => false  
26 - end  
27 -  
28 create_table "issues", :force => true do |t| 16 create_table "issues", :force => true do |t|
29 t.string "title" 17 t.string "title"
30 t.integer "assignee_id" 18 t.integer "assignee_id"
@@ -145,4 +133,11 @@ ActiveRecord::Schema.define(:version =&gt; 20111220190817) do @@ -145,4 +133,11 @@ ActiveRecord::Schema.define(:version =&gt; 20111220190817) do
145 t.integer "project_access", :default => 0, :null => false 133 t.integer "project_access", :default => 0, :null => false
146 end 134 end
147 135
  136 + create_table "web_hooks", :force => true do |t|
  137 + t.string "url"
  138 + t.integer "project_id"
  139 + t.datetime "created_at"
  140 + t.datetime "updated_at"
  141 + end
  142 +
148 end 143 end
lib/gitlabhq/gitolite.rb
@@ -43,14 +43,14 @@ module Gitlabhq @@ -43,14 +43,14 @@ module Gitlabhq
43 43
44 def destroy_project(project) 44 def destroy_project(project)
45 FileUtils.rm_rf(project.path_to_repo) 45 FileUtils.rm_rf(project.path_to_repo)
46 - 46 +
47 ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) 47 ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
48 conf = ga_repo.config 48 conf = ga_repo.config
49 conf.rm_repo(project.path) 49 conf.rm_repo(project.path)
50 ga_repo.save 50 ga_repo.save
51 end 51 end
52 52
53 - #update or create 53 + #update or create
54 def update_keys(user, key) 54 def update_keys(user, key)
55 File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) } 55 File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) }
56 end 56 end
lib/post-receive-hook 0 → 100755
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +#!/bin/bash
  2 +
  3 +# This file was placed here by Gitlab. It makes sure that your pushed commits
  4 +# will be processed properly.
  5 +
  6 +while read oldrev newrev ref
  7 +do
  8 + # For every branch or tag that was pushed, create a Resque job in redis.
  9 + pwd=`pwd`
  10 + reponame=`basename "$pwd" | cut -d. -f1`
  11 + env -i redis-cli rpush "resque:queue:post-receive" "{\"class\":\"PostReceive\",\"args\":[\"$reponame\",\"$oldrev\",\"$newrev\",\"$ref\"]}" > /dev/null 2>&1
  12 +done
lib/tasks/resque.rake 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require 'resque/tasks'
@@ -17,15 +17,13 @@ module Utils @@ -17,15 +17,13 @@ module Utils
17 end 17 end
18 18
19 module CharEncode 19 module CharEncode
20 - def encode(string)  
21 - return '' unless string  
22 - cd = CharDet.detect(string)  
23 - if cd.confidence > 0.6  
24 - string.force_encoding(cd.encoding) 20 + def encode(content)
  21 + content ||= ''
  22 + detection = CharlockHolmes::EncodingDetector.detect(content)
  23 + if hash = detection
  24 + content = CharlockHolmes::Converter.convert(content, hash[:encoding], 'UTF-8') if hash[:encoding]
25 end 25 end
26 - string.encode("utf-8", :undef => :replace, :replace => "?", :invalid => :replace)  
27 - rescue  
28 - "Invalid Encoding" 26 + content
29 end 27 end
30 end 28 end
31 29
public/favicon.ico
No preview for this file type
spec/factories.rb
@@ -55,3 +55,7 @@ Factory.add(:key, Key) do |obj| @@ -55,3 +55,7 @@ Factory.add(:key, Key) do |obj|
55 obj.title = "Example key" 55 obj.title = "Example key"
56 obj.key = File.read(File.join(Rails.root, "db", "pkey.example")) 56 obj.key = File.read(File.join(Rails.root, "db", "pkey.example"))
57 end 57 end
  58 +
  59 +Factory.add(:web_hook, WebHook) do |obj|
  60 + obj.url = Faker::Internet.url
  61 +end
spec/helpers/commit_helper_spec.rb 0 → 100644
@@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
  1 +require "spec_helper"
  2 +include Haml::Helpers
  3 +
  4 +describe CommitsHelper do
  5 +
  6 + before do
  7 + @project = Factory :project
  8 + @other_project = Factory :project, :path => "OtherPath", :code => "OtherCode"
  9 + @fake_user = Factory :user
  10 + @valid_issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @project
  11 + @invalid_issue = Factory :issue, :assignee => @fake_user, :author => @fake_user, :project => @other_project
  12 + end
  13 +
  14 + it "should provides return message untouched if no issue number present" do
  15 + message = "Dummy message without issue number"
  16 +
  17 + commit_msg_with_link_to_issues(@project, message).should eql message
  18 + end
  19 +
  20 + it "should returns message handled by preserve" do
  21 + message = "My brand new
  22 + Commit on multiple
  23 + lines !"
  24 +
  25 + #\n are converted to &#x000A as specified in preserve_rspec
  26 + expected = "My brand new&#x000A; Commit on multiple&#x000A; lines !"
  27 +
  28 + commit_msg_with_link_to_issues(@project, message).should eql expected
  29 + end
  30 +
  31 + it "should returns empty string if message undefined" do
  32 + commit_msg_with_link_to_issues(@project, nil).should eql ''
  33 + end
  34 +
  35 + it "should returns link_to issue for one valid issue in message" do
  36 + issue_id = @valid_issue.id
  37 + message = "One commit message ##{issue_id}"
  38 + expected = "One commit message <a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a>"
  39 +
  40 + commit_msg_with_link_to_issues(@project, message).should eql expected
  41 + end
  42 +
  43 + it "should returns message untouched for one invalid issue in message" do
  44 + issue_id = @invalid_issue.id
  45 + message = "One commit message ##{issue_id}"
  46 +
  47 + commit_msg_with_link_to_issues(@project, message).should eql message
  48 + end
  49 +
  50 + it "should handle multiple issue references in commit message" do
  51 + issue_id = @valid_issue.id
  52 + invalid_issue_id = @invalid_issue.id
  53 +
  54 + message = "One big commit message with a valid issue ##{issue_id} and an invalid one ##{invalid_issue_id}.
  55 + We reference valid ##{issue_id} multiple times (##{issue_id}) as the invalid ##{invalid_issue_id} is also
  56 + referenced another time (##{invalid_issue_id})"
  57 +
  58 + expected = "One big commit message with a valid issue <a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a>"+
  59 + " and an invalid one ##{invalid_issue_id}.&#x000A; "+
  60 + "We reference valid <a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a> multiple times "+
  61 + "(<a href=\"/#{@project.code}/issues/#{issue_id}\">##{issue_id}</a>) "+
  62 + "as the invalid ##{invalid_issue_id} is also&#x000A; referenced another time (##{invalid_issue_id})"
  63 +
  64 + commit_msg_with_link_to_issues(@project, message).should eql expected
  65 + end
  66 +
  67 +end
0 \ No newline at end of file 68 \ No newline at end of file
spec/models/project_spec.rb
@@ -7,6 +7,7 @@ describe Project do @@ -7,6 +7,7 @@ describe Project do
7 it { should have_many(:issues) } 7 it { should have_many(:issues) }
8 it { should have_many(:notes) } 8 it { should have_many(:notes) }
9 it { should have_many(:snippets) } 9 it { should have_many(:snippets) }
  10 + it { should have_many(:web_hooks).dependent(:destroy) }
10 end 11 end
11 12
12 describe "Validation" do 13 describe "Validation" do
@@ -33,6 +34,7 @@ describe Project do @@ -33,6 +34,7 @@ describe Project do
33 it { should respond_to(:repo) } 34 it { should respond_to(:repo) }
34 it { should respond_to(:tags) } 35 it { should respond_to(:tags) }
35 it { should respond_to(:commit) } 36 it { should respond_to(:commit) }
  37 + it { should respond_to(:commits_between) }
36 end 38 end
37 39
38 it "should not allow 'gitolite-admin' as repo name" do 40 it "should not allow 'gitolite-admin' as repo name" do
@@ -50,6 +52,11 @@ describe Project do @@ -50,6 +52,11 @@ describe Project do
50 project.path_to_repo.should == File.join(Rails.root, "tmp", "tests", "somewhere") 52 project.path_to_repo.should == File.join(Rails.root, "tmp", "tests", "somewhere")
51 end 53 end
52 54
  55 + it "returns the full web URL for this repo" do
  56 + project = Project.new(:code => "somewhere")
  57 + project.web_url.should == "#{GIT_HOST['host']}/somewhere"
  58 + end
  59 +
53 describe :valid_repo? do 60 describe :valid_repo? do
54 it "should be valid repo" do 61 it "should be valid repo" do
55 project = Factory :project 62 project = Factory :project
@@ -62,6 +69,106 @@ describe Project do @@ -62,6 +69,106 @@ describe Project do
62 end 69 end
63 end 70 end
64 71
  72 + describe "web hooks" do
  73 + let(:project) { Factory :project }
  74 +
  75 + context "with no web hooks" do
  76 + it "raises no errors" do
  77 + lambda {
  78 + project.execute_web_hooks('oldrev', 'newrev', 'ref')
  79 + }.should_not raise_error
  80 + end
  81 + end
  82 +
  83 + context "with web hooks" do
  84 + before do
  85 + @webhook = Factory(:web_hook)
  86 + @webhook_2 = Factory(:web_hook)
  87 + project.web_hooks << [@webhook, @webhook_2]
  88 + end
  89 +
  90 + it "executes multiple web hook" do
  91 + @webhook.should_receive(:execute).once
  92 + @webhook_2.should_receive(:execute).once
  93 +
  94 + project.execute_web_hooks('oldrev', 'newrev', 'refs/heads/master')
  95 + end
  96 + end
  97 +
  98 + context "does not execute web hooks" do
  99 + before do
  100 + @webhook = Factory(:web_hook)
  101 + project.web_hooks << [@webhook]
  102 + end
  103 +
  104 + it "when pushing a branch for the first time" do
  105 + @webhook.should_not_receive(:execute)
  106 + project.execute_web_hooks('00000000000000000000000000000000', 'newrev', 'refs/heads/master')
  107 + end
  108 +
  109 + it "when pushing tags" do
  110 + @webhook.should_not_receive(:execute)
  111 + project.execute_web_hooks('oldrev', 'newrev', 'refs/tags/v1.0.0')
  112 + end
  113 + end
  114 +
  115 + context "when pushing new branches" do
  116 +
  117 + end
  118 +
  119 + context "when gathering commit data" do
  120 + before do
  121 + @oldrev, @newrev, @ref = project.fresh_commits(2).last.sha, project.fresh_commits(2).first.sha, 'refs/heads/master'
  122 + @commit = project.fresh_commits(2).first
  123 +
  124 + # Fill nil/empty attributes
  125 + project.description = "This is a description"
  126 +
  127 + @data = project.web_hook_data(@oldrev, @newrev, @ref)
  128 + end
  129 +
  130 + subject { @data }
  131 +
  132 + it { should include(before: @oldrev) }
  133 + it { should include(after: @newrev) }
  134 + it { should include(ref: @ref) }
  135 +
  136 + context "with repository data" do
  137 + subject { @data[:repository] }
  138 +
  139 + it { should include(name: project.name) }
  140 + it { should include(url: project.web_url) }
  141 + it { should include(description: project.description) }
  142 + it { should include(homepage: project.web_url) }
  143 + it { should include(private: project.private?) }
  144 + end
  145 +
  146 + context "with commits" do
  147 + subject { @data[:commits] }
  148 +
  149 + it { should be_an(Array) }
  150 + it { should have(1).element }
  151 +
  152 + context "the commit" do
  153 + subject { @data[:commits].first }
  154 +
  155 + it { should include(id: @commit.id) }
  156 + it { should include(message: @commit.safe_message) }
  157 + it { should include(timestamp: @commit.date.xmlschema) }
  158 + it { should include(url: "http://localhost/#{project.code}/commits/#{@commit.id}") }
  159 +
  160 + context "with a author" do
  161 + subject { @data[:commits].first[:author] }
  162 +
  163 + it { should include(name: @commit.author_name) }
  164 + it { should include(email: @commit.author_email) }
  165 + end
  166 + end
  167 + end
  168 +
  169 + end
  170 + end
  171 +
65 describe "updates" do 172 describe "updates" do
66 let(:project) { Factory :project } 173 let(:project) { Factory :project }
67 174
@@ -107,6 +214,21 @@ describe Project do @@ -107,6 +214,21 @@ describe Project do
107 it { project.fresh_commits.last.id.should == "0dac878dbfe0b9c6104a87d65fe999149a8d862c" } 214 it { project.fresh_commits.last.id.should == "0dac878dbfe0b9c6104a87d65fe999149a8d862c" }
108 end 215 end
109 216
  217 + describe "commits_between" do
  218 + let(:project) { Factory :project }
  219 +
  220 + subject do
  221 + commits = project.commits_between("a6d1d4aca0c85816ddfd27d93773f43a31395033",
  222 + "2fb376f61875b58bceee0492e270e9c805294b1a")
  223 + commits.map { |c| c.id }
  224 + end
  225 +
  226 + it { should have(2).elements }
  227 + it { should include("2fb376f61875b58bceee0492e270e9c805294b1a") }
  228 + it { should include("4571e226fbcd7be1af16e9fa1e13b7ac003bebdf") }
  229 + it { should_not include("a6d1d4aca0c85816ddfd27d93773f43a31395033") }
  230 + end
  231 +
110 describe "Git methods" do 232 describe "Git methods" do
111 let(:project) { Factory :project } 233 let(:project) { Factory :project }
112 234
spec/models/web_hook_spec.rb 0 → 100644
@@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
  1 +require 'spec_helper'
  2 +
  3 +describe WebHook do
  4 + describe "Associations" do
  5 + it { should belong_to :project }
  6 + end
  7 +
  8 + describe "Validations" do
  9 + it { should validate_presence_of(:url) }
  10 +
  11 + context "url format" do
  12 + it { should allow_value("http://example.com").for(:url) }
  13 + it { should allow_value("https://excample.com").for(:url) }
  14 + it { should allow_value("http://test.com/api").for(:url) }
  15 + it { should allow_value("http://test.com/api?key=abc").for(:url) }
  16 + it { should allow_value("http://test.com/api?key=abc&type=def").for(:url) }
  17 +
  18 + it { should_not allow_value("example.com").for(:url) }
  19 + it { should_not allow_value("ftp://example.com").for(:url) }
  20 + it { should_not allow_value("herp-and-derp").for(:url) }
  21 + end
  22 + end
  23 +
  24 + describe "execute" do
  25 + before(:each) do
  26 + @webhook = Factory :web_hook
  27 + @project = Factory :project
  28 + @project.web_hooks << [@webhook]
  29 + @data = { before: 'oldrev', after: 'newrev', ref: 'ref'}
  30 +
  31 + WebMock.stub_request(:post, @webhook.url)
  32 + end
  33 +
  34 + it "POSTs to the web hook URL" do
  35 + @webhook.execute(@data)
  36 + WebMock.should have_requested(:post, @webhook.url).once
  37 + end
  38 +
  39 + it "POSTs the data as JSON" do
  40 + json = @data.to_json
  41 +
  42 + @webhook.execute(@data)
  43 + WebMock.should have_requested(:post, @webhook.url).with(body: json).once
  44 + end
  45 +
  46 + it "catches exceptions" do
  47 + WebHook.should_receive(:post).and_raise("Some HTTP Post error")
  48 +
  49 + lambda {
  50 + @webhook.execute(@data)
  51 + }.should_not raise_error
  52 + end
  53 + end
  54 +end
spec/requests/dashboard_spec.rb
1 require 'spec_helper' 1 require 'spec_helper'
2 2
3 describe "Dashboard" do 3 describe "Dashboard" do
4 - before { login_as :user } 4 + before do
  5 + @project = Factory :project
  6 + @user = User.create(:email => "test917@mail.com",
  7 + :name => "John Smith",
  8 + :password => "123456",
  9 + :password_confirmation => "123456")
  10 + @project.add_access(@user, :read, :write)
  11 + login_with(@user)
  12 + end
5 13
6 describe "GET /dashboard" do 14 describe "GET /dashboard" do
7 before do 15 before do
8 - @project = Factory :project  
9 - @project.add_access(@user, :read, :write)  
10 visit dashboard_path 16 visit dashboard_path
11 end 17 end
12 18
@@ -20,12 +26,14 @@ describe &quot;Dashboard&quot; do @@ -20,12 +26,14 @@ describe &quot;Dashboard&quot; do
20 end 26 end
21 end 27 end
22 28
23 - it "should have news feed" do  
24 - within "#news-feed" do  
25 - page.should have_content("commit")  
26 - page.should have_content(@project.commit.author.name)  
27 - page.should have_content(@project.commit.safe_message)  
28 - end  
29 - end 29 + # Temporary disabled cause of travis
  30 + # TODO: fix or rewrite
  31 + #it "should have news feed" do
  32 + #within "#news-feed" do
  33 + #page.should have_content("commit")
  34 + #page.should have_content(@project.commit.author.name)
  35 + #page.should have_content(@project.commit.safe_message)
  36 + #end
  37 + #end
30 end 38 end
31 end 39 end
spec/requests/projects_tree_perfomance_spec.rb
1 -require 'spec_helper'  
2 -require 'benchmark'  
3 -  
4 -describe "Projects" do  
5 - before { login_as :user }  
6 -  
7 - describe "GET /projects/tree" do  
8 - describe "head" do  
9 - before do  
10 - @project = Factory :project  
11 - @project.add_access(@user, :read)  
12 -  
13 - end  
14 -  
15 - it "should be fast" do  
16 - time = Benchmark.realtime do  
17 - visit tree_project_ref_path(@project, @project.root_ref)  
18 - end  
19 - (time < 1.0).should be_true  
20 - end  
21 - end  
22 -  
23 - describe ValidCommit::ID do  
24 - before do  
25 - @project = Factory :project  
26 - @project.add_access(@user, :read)  
27 - end  
28 -  
29 - it "should be fast" do  
30 - time = Benchmark.realtime do  
31 - visit tree_project_ref_path(@project, ValidCommit::ID)  
32 - end  
33 - (time < 1.0).should be_true  
34 - end  
35 - end  
36 - end  
37 -end 1 +#require 'spec_helper'
  2 +#require 'benchmark'
  3 +#
  4 +#describe "Projects" do
  5 +# before { login_as :user }
  6 +#
  7 +# describe "GET /projects/tree" do
  8 +# describe "head" do
  9 +# before do
  10 +# @project = Factory :project
  11 +# @project.add_access(@user, :read)
  12 +# end
  13 +#
  14 +# it "should be fast" do
  15 +# time = Benchmark.realtime do
  16 +# visit tree_project_ref_path(@project, @project.root_ref)
  17 +# end
  18 +# (time < 1.0).should be_true
  19 +# end
  20 +# end
  21 +#
  22 +# describe ValidCommit::ID do
  23 +# before do
  24 +# @project = Factory :project
  25 +# @project.add_access(@user, :read)
  26 +# end
  27 +#
  28 +# it "should be fast" do
  29 +# time = Benchmark.realtime do
  30 +# visit tree_project_ref_path(@project, ValidCommit::ID)
  31 +# end
  32 +# (time < 1.0).should be_true
  33 +# end
  34 +# end
  35 +# end
  36 +#end
spec/spec_helper.rb
@@ -8,6 +8,7 @@ require &#39;rspec/rails&#39; @@ -8,6 +8,7 @@ require &#39;rspec/rails&#39;
8 require 'capybara/rails' 8 require 'capybara/rails'
9 require 'capybara/rspec' 9 require 'capybara/rspec'
10 require 'capybara/dsl' 10 require 'capybara/dsl'
  11 +require 'webmock/rspec'
11 require 'factories' 12 require 'factories'
12 require 'monkeypatch' 13 require 'monkeypatch'
13 14
@@ -48,6 +49,8 @@ RSpec.configure do |config| @@ -48,6 +49,8 @@ RSpec.configure do |config|
48 end 49 end
49 50
50 DatabaseCleaner.start 51 DatabaseCleaner.start
  52 +
  53 + WebMock.disable_net_connect!(allow_localhost: true)
51 end 54 end
52 55
53 config.after do 56 config.after do
spec/workers/post_receive_spec.rb 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +require 'spec_helper'
  2 +
  3 +describe PostReceive do
  4 +
  5 + context "as a resque worker" do
  6 + it "reponds to #perform" do
  7 + PostReceive.should respond_to(:perform)
  8 + end
  9 + end
  10 +
  11 + context "web hooks" do
  12 + let(:project) { Factory :project }
  13 +
  14 + it "it retrieves the correct project" do
  15 + Project.should_receive(:find_by_path).with(project.path)
  16 + PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master')
  17 + end
  18 +
  19 + it "asks the project to execute web hooks" do
  20 + Project.stub(find_by_path: project)
  21 + project.should_receive(:execute_web_hooks).with('sha-old', 'sha-new', 'refs/heads/master')
  22 +
  23 + PostReceive.perform(project.path, 'sha-old', 'sha-new', 'refs/heads/master')
  24 + end
  25 + end
  26 +end