integrity 0.1.9.2 → 0.1.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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