integrity 0.1.9.2 → 0.1.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,11 +2,15 @@ module Integrity
2
2
  class Notifier
3
3
  class Base
4
4
  def self.notify_of_build(build, config)
5
- Timeout.timeout(8) { new(build, config).deliver! }
5
+ Integrity.log "Notifying of build #{build.commit.short_identifier} using the #{self.class} notifier"
6
+ Timeout.timeout(8) { new(build.commit, config).deliver! }
7
+ rescue Timeout::Error
8
+ Integrity.log "#{notifier.name} notifier timed out"
9
+ false
6
10
  end
7
11
 
8
12
  def self.to_haml
9
- raise NoMethodError, "you need to implement this method in your notifier"
13
+ raise NotImplementedError, "you need to implement this method in your notifier"
10
14
  end
11
15
 
12
16
  attr_reader :commit
@@ -22,7 +26,7 @@ module Integrity
22
26
  end
23
27
 
24
28
  def deliver!
25
- raise NoMethodError, "you need to implement this method in your notifier"
29
+ raise NotImplementedError, "you need to implement this method in your notifier"
26
30
  end
27
31
 
28
32
  def short_message
@@ -1,7 +1,13 @@
1
+ require "integrity/project/notifiers"
2
+ require "integrity/project/push"
3
+
1
4
  module Integrity
2
5
  class Project
3
6
  include DataMapper::Resource
4
7
 
8
+ include Helpers::Notifiers
9
+ include Helpers::Push
10
+
5
11
  property :id, Integer, :serial => true
6
12
  property :name, String, :nullable => false
7
13
  property :permalink, String
@@ -17,7 +23,7 @@ module Integrity
17
23
  has n, :notifiers, :class_name => "Integrity::Notifier"
18
24
 
19
25
  before :save, :set_permalink
20
- before :destroy, :delete_code
26
+ before :destroy, :delete_working_directory
21
27
 
22
28
  validates_is_unique :name
23
29
 
@@ -32,44 +38,17 @@ module Integrity
32
38
  def build(commit_identifier="HEAD")
33
39
  commit_identifier = head_of_remote_repo if commit_identifier == "HEAD"
34
40
  commit = find_or_create_commit_with_identifier(commit_identifier)
35
- commit.queue_build
36
- end
37
-
38
- def push(payload)
39
- payload = parse_payload(payload)
40
- raise ArgumentError unless valid_payload?(payload)
41
-
42
- commits =
43
- if Integrity.config[:build_all_commits]
44
- payload["commits"]
45
- else
46
- [ payload["commits"].first ]
47
- end
48
-
49
- commits.each do |commit_data|
50
- create_commit_from(commit_data)
51
- build(commit_data["id"])
52
- end
41
+ Build.queue(commit)
53
42
  end
54
43
 
55
44
  def last_commit
56
45
  commits.first(:project_id => id, :order => [:committed_at.desc])
57
46
  end
58
47
 
59
- def last_build
60
- warn "Project#last_build is deprecated, use Project#last_commit (#{caller[0]})"
61
- last_commit
62
- end
63
-
64
48
  def previous_commits
65
49
  commits.all(:project_id => id, :order => [:committed_at.desc]).tap {|commits| commits.shift }
66
50
  end
67
51
 
68
- def previous_builds
69
- warn "Project#previous_builds is deprecated, use Project#previous_commits (#{caller[0]})"
70
- previous_commits
71
- end
72
-
73
52
  def status
74
53
  last_commit && last_commit.status
75
54
  end
@@ -85,19 +64,6 @@ module Integrity
85
64
  end)
86
65
  end
87
66
 
88
- def config_for(notifier)
89
- notifier = notifiers.first(:name => notifier.to_s.split(/::/).last, :project_id => id)
90
- notifier.blank? ? {} : notifier.config
91
- end
92
-
93
- def notifies?(notifier)
94
- !notifiers.first(:name => notifier.to_s.split(/::/).last, :project_id => id).blank?
95
- end
96
-
97
- def enable_notifiers(*args)
98
- Notifier.enable_notifiers(id, *args)
99
- end
100
-
101
67
  private
102
68
  def find_or_create_commit_with_identifier(commit_identifier)
103
69
  # We abuse +committed_at+ here setting it to Time.now because we use it
@@ -116,13 +82,6 @@ module Integrity
116
82
  SCM.new(uri, branch).head
117
83
  end
118
84
 
119
- def create_commit_from(data)
120
- commits.create(:identifier => data["id"],
121
- :author => "#{data["author"]["name"]} <#{data["author"]["email"]}>",
122
- :message => data["message"],
123
- :committed_at => data["timestamp"])
124
- end
125
-
126
85
  def set_permalink
127
86
  self.permalink = (name || "").downcase.
128
87
  gsub(/'s/, "s").
@@ -131,23 +90,11 @@ module Integrity
131
90
  gsub(/-*$/, "")
132
91
  end
133
92
 
134
- def delete_code
93
+ def delete_working_directory
135
94
  commits.all(:project_id => id).destroy!
136
- ProjectBuilder.new(self).delete_code
95
+ ProjectBuilder.delete_working_directory(self)
137
96
  rescue SCM::SCMUnknownError => error
138
97
  Integrity.log "Problem while trying to deleting code: #{error}"
139
98
  end
140
-
141
- def valid_payload?(payload)
142
- payload && payload["ref"].to_s.include?(branch) &&
143
- !payload["commits"].nil? &&
144
- !payload["commits"].to_a.empty?
145
- end
146
-
147
- def parse_payload(payload)
148
- JSON.parse(payload.to_s)
149
- rescue JSON::ParserError
150
- false
151
- end
152
99
  end
153
100
  end
@@ -0,0 +1,33 @@
1
+ module Integrity
2
+ class Project
3
+ module Helpers
4
+ module Notifiers
5
+ def notifies?(notifier)
6
+ return false unless notifier = notifiers.first(:name => notifier)
7
+
8
+ notifier.enabled?
9
+ end
10
+
11
+ def enabled_notifiers
12
+ notifiers.all(:enabled => true)
13
+ end
14
+
15
+ def config_for(notifier)
16
+ notifier = notifiers.first(:name => notifier)
17
+ notifier ? notifier.config : {}
18
+ end
19
+
20
+ def update_notifiers(to_enable, config)
21
+ config.each_pair { |name, config|
22
+ notifier = notifiers.first(:name => name)
23
+ notifier ||= notifiers.new(:name => name)
24
+
25
+ notifier.enabled = to_enable.include?(name)
26
+ notifier.config = config
27
+ notifier.save
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ module Integrity
2
+ class Project
3
+ module Helpers
4
+ module Push
5
+ def push(payload)
6
+ payload = parse_payload(payload)
7
+ raise ArgumentError unless valid_payload?(payload)
8
+
9
+ commits =
10
+ if Integrity.config[:build_all_commits]
11
+ payload["commits"]
12
+ else
13
+ [ payload["commits"].first ]
14
+ end
15
+
16
+ commits.each do |commit_data|
17
+ create_commit_from(commit_data)
18
+ build(commit_data["id"])
19
+ end
20
+ end
21
+
22
+ private
23
+ def create_commit_from(data)
24
+ commits.create(:identifier => data["id"],
25
+ :author => "#{data["author"]["name"]} <#{data["author"]["email"]}>",
26
+ :message => data["message"],
27
+ :committed_at => data["timestamp"])
28
+ end
29
+
30
+ def valid_payload?(payload)
31
+ payload && payload["ref"].to_s.include?(branch) &&
32
+ !payload["commits"].nil? &&
33
+ !payload["commits"].to_a.empty?
34
+ end
35
+
36
+ def parse_payload(payload)
37
+ JSON.parse(payload.to_s)
38
+ rescue JSON::ParserError
39
+ false
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,24 +1,45 @@
1
+ require "forwardable"
2
+
1
3
  module Integrity
2
4
  class ProjectBuilder
5
+ extend Forwardable
6
+
7
+ attr_accessor :project, :scm
8
+ def_delegators :project, :name, :uri, :command, :branch
9
+
10
+ def self.build(commit)
11
+ new(commit.project).build(commit)
12
+ end
13
+
14
+ def self.delete_working_directory(project)
15
+ new(project).delete_code
16
+ end
17
+
3
18
  def initialize(project)
4
19
  @project = project
5
- @uri = project.uri
6
- @build_script = project.command
7
- @branch = project.branch
8
- @scm = SCM.new(@uri, @branch, export_directory)
20
+ @scm = SCM.new(uri, branch, export_directory)
9
21
  end
10
22
 
11
23
  def build(commit)
12
- @commit = commit
13
- @build = commit.build
14
- @build.start!
15
- Integrity.log "Building #{commit.identifier} (#{@branch}) of #{@project.name} in #{export_directory} using #{@scm.name}"
16
- @scm.with_revision(commit.identifier) { run_build_script }
17
- @build
24
+ build = commit.build
25
+ build.start!
26
+
27
+ Integrity.log "Building #{commit.identifier} (#{branch}) of #{name} in" +
28
+ "#{export_directory} using #{scm.name}"
29
+
30
+ scm.with_revision(commit.identifier) do
31
+ Integrity.log "Running `#{command}` in #{scm.working_directory}"
32
+
33
+ IO.popen("(cd #{scm.working_directory} && #{command}) 2>&1", "r") {
34
+ |output| build.output = output.read }
35
+ build.successful = $?.success?
36
+ end
37
+
38
+ build
18
39
  ensure
19
- @build.complete!
20
- @commit.update_attributes(@scm.info(commit.identifier))
21
- send_notifications
40
+ build.complete!
41
+ commit.update_attributes(scm.info(commit.identifier) || {})
42
+ project.notifiers.each { |notifier| notifier.notify_of_build(build) }
22
43
  end
23
44
 
24
45
  def delete_code
@@ -28,29 +49,8 @@ module Integrity
28
49
  end
29
50
 
30
51
  private
31
- def send_notifications
32
- @project.notifiers.each do |notifier|
33
- begin
34
- Integrity.log "Notifying of build #{@commit.short_identifier} using the #{notifier.name} notifier"
35
- notifier.notify_of_build @commit
36
- rescue Timeout::Error
37
- Integrity.log "#{notifier.name} notifier timed out"
38
- next
39
- end
40
- end
41
- end
42
-
43
52
  def export_directory
44
- Integrity.config[:export_directory] / "#{SCM.working_tree_path(@uri)}-#{@branch}"
45
- end
46
-
47
- def run_build_script
48
- Integrity.log "Running `#{@build_script}` in #{@scm.working_directory}"
49
-
50
- IO.popen "(cd #{@scm.working_directory} && #{@build_script}) 2>&1", "r" do |pipe|
51
- @build.output = pipe.read
52
- end
53
- @build.successful = $?.success?
53
+ Integrity.config[:export_directory] / "#{SCM.working_tree_path(uri)}-#{branch}"
54
54
  end
55
55
  end
56
56
  end
@@ -48,6 +48,10 @@ class BrowsePublicProjectsTest < Test::Unit::AcceptanceTestCase
48
48
  click_link "My Test Project"
49
49
 
50
50
  assert_have_tag("h1", :content => "My Test Project")
51
+
52
+ # He can then go back to the project listing
53
+ click_link "projects"
54
+ assert_have_tag("a", :content => "My Test Project")
51
55
  end
52
56
 
53
57
  scenario "a user gets a 404 when browsing to an unexisting project" do
@@ -1,7 +1,9 @@
1
1
  require File.dirname(__FILE__) + "/../helpers/acceptance"
2
- require "helpers/acceptance/textfile_notifier"
2
+ require "helpers/acceptance/notifier_helper"
3
3
 
4
4
  class BuildNotificationsTest < Test::Unit::AcceptanceTestCase
5
+ include NotifierHelper
6
+
5
7
  story <<-EOS
6
8
  As an administrator,
7
9
  I want to setup notifiers on my projects
@@ -12,11 +14,14 @@ class BuildNotificationsTest < Test::Unit::AcceptanceTestCase
12
14
  # This is needed before any available notifier is unset
13
15
  # in the global #before
14
16
  load "helpers/acceptance/textfile_notifier.rb"
17
+ load "helpers/acceptance/email_notifier.rb"
18
+ Notifier.register(Integrity::Notifier::Textfile)
19
+ Notifier.register(Integrity::Notifier::Email)
15
20
  end
16
21
 
17
- scenario "an admin sets up a notifier for a project that didn't have any" do
22
+ scenario "an admin sets up a notifier and issue a manual build" do
18
23
  git_repo(:my_test_project).add_successful_commit
19
- Project.gen(:my_test_project, :notifiers => [], :uri => git_repo(:my_test_project).path)
24
+ Project.gen(:my_test_project, :uri => git_repo(:my_test_project).path)
20
25
  rm_f "/tmp/textfile_notifications.txt"
21
26
 
22
27
  login_as "admin", "test"
@@ -33,10 +38,58 @@ class BuildNotificationsTest < Test::Unit::AcceptanceTestCase
33
38
  notification = File.read("/tmp/textfile_notifications.txt")
34
39
  notification.should =~ /=== Built #{git_repo(:my_test_project).short_head} successfully ===/
35
40
  notification.should =~ /Build #{git_repo(:my_test_project).head} was successful/
36
- notification.should =~ %r(http://www.example.com/my-test-project/commits/#{git_repo(:my_test_project).head})
41
+ notification.should =~
42
+ %r(http://www.example.com/my-test-project/commits/#{git_repo(:my_test_project).head})
37
43
  notification.should =~ /Commit Author: John Doe/
38
44
  notification.should =~ /Commit Date: (.+)/
39
45
  notification.should =~ /Commit Message: This commit will work/
40
46
  notification.should =~ /Build Output:\n\nRunning tests...\n/
41
47
  end
48
+
49
+ scenario "an admin can setup a notifier without enabling it" do
50
+ Project.gen(:integrity)
51
+
52
+ login_as "admin", "test"
53
+
54
+ visit "/integrity"
55
+ click_link "Edit Project"
56
+ fill_in_email_notifier
57
+ click_button "Update Project"
58
+
59
+ visit "/integrity/edit"
60
+ assert_have_email_notifier
61
+ end
62
+
63
+ scenario "an admin configures various notifiers accros multiple projects" do
64
+ Project.first(:permalink => "integrity").should be_nil
65
+
66
+ login_as "admin", "test"
67
+
68
+ visit "/"
69
+
70
+ add_project "Integrity", "git://github.com/foca/integrity.git"
71
+ click_link "projects"
72
+
73
+ add_project "Webrat", "git://github.com/brynary/webrat.git"
74
+ click_link "projects"
75
+
76
+ add_project "Rails", "git://github.com/rails/rails.git"
77
+ click_link "projects"
78
+
79
+ edit_project "integrity"
80
+ edit_project "webrat"
81
+ edit_project "rails"
82
+
83
+ visit "/integrity"
84
+ click_link "Edit Project"
85
+ assert_have_email_notifier
86
+
87
+ visit "/webrat"
88
+ click_link "Edit Project"
89
+ assert_have_email_notifier
90
+
91
+ visit "/rails"
92
+ click_link "Edit Project"
93
+ assert_have_email_notifier
94
+ end
42
95
  end
@@ -24,7 +24,10 @@ class InstallerTest < Test::Unit::AcceptanceTestCase
24
24
  end
25
25
 
26
26
  scenario "Installing integrity into a given directory" do
27
- assert install.include?("Awesome")
27
+ post_install_message = install
28
+
29
+ assert post_install_message.include?("Awesome")
30
+ assert post_install_message.include?("integrity migrate_db #{root.join("config.yml")}")
28
31
 
29
32
  assert root.join("builds").directory?
30
33
  assert root.join("log").directory?
@@ -1,27 +1,24 @@
1
1
  $:.unshift File.dirname(__FILE__) + "/../lib", File.dirname(__FILE__)
2
2
 
3
- %w(test/unit
4
- context
5
- pending
6
- matchy
7
- storyteller
8
- webrat/sinatra
9
- rr
10
- mocha
11
- dm-sweatshop).each { |dependency|
12
- begin
13
- require dependency
14
- rescue LoadError => e
15
- puts "You're missing some gems required to run the tests."
16
- puts "Please run `rake test:setup`"
17
- puts "NOTE: You'll probably need to run that command as root or with sudo."
18
-
19
- puts "Thanks :)"
20
- puts
21
-
22
- raise
23
- end
24
- }
3
+ require "rubygems"
4
+
5
+ require "test/unit"
6
+ require "rr"
7
+ require "mocha"
8
+ require "dm-sweatshop"
9
+ require "webrat/sinatra"
10
+
11
+ gem "jeremymcanally-context"
12
+ gem "jeremymcanally-matchy"
13
+ gem "jeremymcanally-pending"
14
+ require "context"
15
+ require "matchy"
16
+ require "pending"
17
+
18
+ require "integrity"
19
+ require "integrity/notifier/test/fixtures"
20
+
21
+ require "helpers/expectations"
25
22
 
26
23
  begin
27
24
  require "ruby-debug"
@@ -29,22 +26,25 @@ begin
29
26
  rescue LoadError
30
27
  end
31
28
 
32
- require "integrity"
33
- require "helpers/expectations"
34
- require "integrity/notifier/test/fixtures"
35
-
36
29
  module TestHelper
37
30
  def ignore_logs!
38
31
  Integrity.config[:log] = "/tmp/integrity.test.log"
39
32
  end
40
33
 
41
- def util_capture
34
+ def capture_stdout
42
35
  output = StringIO.new
43
36
  $stdout = output
44
37
  yield
45
38
  $stdout = STDOUT
46
39
  output
47
40
  end
41
+
42
+ def silence_warnings
43
+ $VERBOSE, v = nil, $VERBOSE
44
+ yield
45
+ ensure
46
+ $VERBOSE = v
47
+ end
48
48
  end
49
49
 
50
50
  class Test::Unit::TestCase
@@ -58,29 +58,25 @@ class Test::Unit::TestCase
58
58
 
59
59
  before(:all) do
60
60
  DataMapper.setup(:default, "sqlite3::memory:")
61
+
62
+ require "integrity/migrations"
61
63
  end
62
64
 
63
65
  before(:each) do
66
+ [Project, Build, Commit, Notifier].each(&:auto_migrate_down!)
67
+ capture_stdout { Integrity.migrate_db }
68
+
64
69
  RR.reset
65
- DataMapper.auto_migrate!
66
- Integrity.instance_variable_set(:@config, nil)
70
+
67
71
  Notifier.available.each { |n|
68
72
  Notifier.send(:remove_const, n.to_s.split(":").last.to_sym)
69
73
  }
70
74
 
71
- repository(:default) do
72
- transaction = DataMapper::Transaction.new(repository)
73
- transaction.begin
74
- repository.adapter.push_transaction(transaction)
75
- end
75
+ Notifier.available.clear
76
+ Integrity.instance_variable_set(:@config, nil)
76
77
  end
77
78
 
78
79
  after(:each) do
79
- repository(:default) do
80
- while repository.adapter.current_transaction
81
- repository.adapter.current_transaction.rollback
82
- repository.adapter.pop_transaction
83
- end
84
- end
80
+ capture_stdout { Integrity::Migrations.migrate_down! }
85
81
  end
86
82
  end