integrity 0.1.8
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.
- data/README.markdown +66 -0
- data/Rakefile +165 -0
- data/VERSION.yml +4 -0
- data/app.rb +138 -0
- data/bin/integrity +4 -0
- data/config/config.sample.ru +31 -0
- data/config/config.sample.yml +38 -0
- data/config/thin.sample.yml +13 -0
- data/integrity.gemspec +76 -0
- data/lib/integrity.rb +80 -0
- data/lib/integrity/build.rb +61 -0
- data/lib/integrity/core_ext/object.rb +6 -0
- data/lib/integrity/core_ext/string.rb +5 -0
- data/lib/integrity/helpers.rb +16 -0
- data/lib/integrity/helpers/authorization.rb +33 -0
- data/lib/integrity/helpers/breadcrumbs.rb +20 -0
- data/lib/integrity/helpers/forms.rb +28 -0
- data/lib/integrity/helpers/pretty_output.rb +45 -0
- data/lib/integrity/helpers/rendering.rb +14 -0
- data/lib/integrity/helpers/resources.rb +13 -0
- data/lib/integrity/helpers/urls.rb +47 -0
- data/lib/integrity/installer.rb +132 -0
- data/lib/integrity/migrations.rb +157 -0
- data/lib/integrity/notifier.rb +50 -0
- data/lib/integrity/notifier/base.rb +55 -0
- data/lib/integrity/project.rb +117 -0
- data/lib/integrity/project_builder.rb +47 -0
- data/lib/integrity/scm.rb +19 -0
- data/lib/integrity/scm/git.rb +83 -0
- data/lib/integrity/scm/git/uri.rb +57 -0
- data/public/buttons.css +82 -0
- data/public/reset.css +7 -0
- data/public/spinner.gif +0 -0
- data/test/helpers.rb +47 -0
- data/test/helpers/acceptance.rb +127 -0
- data/test/helpers/acceptance/git_helper.rb +99 -0
- data/test/helpers/acceptance/textfile_notifier.rb +26 -0
- data/test/helpers/expectations.rb +5 -0
- data/test/helpers/expectations/be_a.rb +23 -0
- data/test/helpers/expectations/change.rb +90 -0
- data/test/helpers/expectations/have.rb +105 -0
- data/test/helpers/expectations/have_tag.rb +128 -0
- data/test/helpers/expectations/predicates.rb +37 -0
- data/test/helpers/fixtures.rb +83 -0
- data/views/_build_info.haml +18 -0
- data/views/build.haml +2 -0
- data/views/error.haml +36 -0
- data/views/home.haml +23 -0
- data/views/integrity.sass +387 -0
- data/views/layout.haml +28 -0
- data/views/new.haml +51 -0
- data/views/not_found.haml +31 -0
- data/views/notifier.haml +7 -0
- data/views/project.builder +21 -0
- data/views/project.haml +28 -0
- data/views/unauthorized.haml +38 -0
- metadata +258 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../integrity"
|
2
|
+
require "thor"
|
3
|
+
|
4
|
+
module Integrity
|
5
|
+
class Installer < Thor
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
desc "install [PATH]",
|
9
|
+
"Copy template files to PATH. Next, go there and edit them."
|
10
|
+
def install(path)
|
11
|
+
@root = File.expand_path(path)
|
12
|
+
|
13
|
+
create_dir_structure
|
14
|
+
copy_template_files
|
15
|
+
edit_template_files
|
16
|
+
create_db(root / "config.yml")
|
17
|
+
after_setup_message
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "create_db [CONFIG]",
|
21
|
+
"Checks the `database_uri` in CONFIG and creates and bootstraps a database for integrity"
|
22
|
+
def create_db(config, direction="up")
|
23
|
+
Integrity.new(config)
|
24
|
+
migrate_db(direction, 1)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "version",
|
28
|
+
"Print the current integrity version"
|
29
|
+
def version
|
30
|
+
puts Integrity.version
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
attr_reader :root
|
35
|
+
|
36
|
+
def migrate_db(direction, level=nil)
|
37
|
+
require "integrity/migrations"
|
38
|
+
|
39
|
+
set_up_migrations unless migrations_already_set_up?
|
40
|
+
add_initial_migration if tables_from_before_migrations_exist?
|
41
|
+
|
42
|
+
case direction
|
43
|
+
when "up" then Integrity::Migrations.migrate_up!(level)
|
44
|
+
when "down" then Integrity::Migrations.migrate_down!(level)
|
45
|
+
else raise ArgumentError, "DIRECTION must be either up or down"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_dir_structure
|
50
|
+
mkdir_p root
|
51
|
+
mkdir_p root / "builds"
|
52
|
+
mkdir_p root / "log"
|
53
|
+
mkdir_p root / "public" # this one is to play nice with Passenger
|
54
|
+
mkdir_p root / "tmp" # this one is to play nice with Passenger
|
55
|
+
end
|
56
|
+
|
57
|
+
def copy_template_files
|
58
|
+
cp Integrity.root / "config" / "config.sample.ru", root / "config.ru"
|
59
|
+
cp Integrity.root / "config" / "config.sample.yml", root / "config.yml"
|
60
|
+
cp Integrity.root / "config" / "thin.sample.yml", root / "thin.yml"
|
61
|
+
end
|
62
|
+
|
63
|
+
def edit_template_files
|
64
|
+
edit_integrity_configuration
|
65
|
+
edit_thin_configuration
|
66
|
+
end
|
67
|
+
|
68
|
+
def edit_integrity_configuration
|
69
|
+
config = File.read(root / "config.yml")
|
70
|
+
config.gsub! %r(sqlite3:///var/integrity.db), "sqlite3://#{root}/integrity.db"
|
71
|
+
config.gsub! %r(/path/to/scm/exports), "#{root}/builds"
|
72
|
+
config.gsub! %r(/var/log), "#{root}/log"
|
73
|
+
File.open(root / "config.yml", "w") { |f| f.puts config }
|
74
|
+
end
|
75
|
+
|
76
|
+
def edit_thin_configuration
|
77
|
+
config = File.read(root / "thin.yml")
|
78
|
+
config.gsub! %r(/apps/integrity), root
|
79
|
+
File.open(root / "thin.yml", 'w') { |f| f.puts config }
|
80
|
+
end
|
81
|
+
|
82
|
+
def after_setup_message
|
83
|
+
puts
|
84
|
+
puts %Q(Awesome! Integrity was installed successfully!)
|
85
|
+
puts
|
86
|
+
puts %Q(If you want to enable notifiers, install the gems and then require them)
|
87
|
+
puts %Q(in #{root}/config.ru)
|
88
|
+
puts
|
89
|
+
puts %Q(For example:)
|
90
|
+
puts
|
91
|
+
puts %Q( sudo gem install -s http://gems.github.com foca-integrity-email)
|
92
|
+
puts
|
93
|
+
puts %Q(And then in #{root}/config.ru add:)
|
94
|
+
puts
|
95
|
+
puts %Q( require "notifier/email")
|
96
|
+
puts
|
97
|
+
puts %Q(Don't forget to tweak #{root / "config.yml"} to your needs.)
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_up_migrations
|
101
|
+
database_adapter.execute %q(CREATE TABLE "migration_info" ("migration_name" VARCHAR(255));)
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_initial_migration
|
105
|
+
database_adapter.execute %q(INSERT INTO "migration_info" ("migration_name") VALUES ("initial"))
|
106
|
+
end
|
107
|
+
|
108
|
+
def tables_from_before_migrations_exist?
|
109
|
+
table_exists?("integrity_projects") &&
|
110
|
+
table_exists?("integrity_builds") &&
|
111
|
+
table_exists?("integrity_notifiers")
|
112
|
+
end
|
113
|
+
|
114
|
+
def migrations_already_set_up?
|
115
|
+
table_exists?("migration_info")
|
116
|
+
end
|
117
|
+
|
118
|
+
def without_pluralizing_table_names
|
119
|
+
database_adapter.resource_naming_convention = DataMapper::NamingConventions::Resource::Underscored
|
120
|
+
yield
|
121
|
+
database_adapter.resource_naming_convention = DataMapper::NamingConventions::Resource::UnderscoredAndPluralized
|
122
|
+
end
|
123
|
+
|
124
|
+
def table_exists?(table_name)
|
125
|
+
database_adapter.storage_exists?(table_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def database_adapter
|
129
|
+
DataMapper.repository(:default).adapter
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require "dm-migrations"
|
2
|
+
require "migration_runner"
|
3
|
+
|
4
|
+
module Integrity
|
5
|
+
class Migrations
|
6
|
+
include DataMapper::Types
|
7
|
+
|
8
|
+
# not strictly necessary, but it makes it clear what is going on.
|
9
|
+
include DataMapper::MigrationRunner
|
10
|
+
|
11
|
+
migration 1, :initial, :verbose => false do
|
12
|
+
up do
|
13
|
+
create_table :integrity_projects do
|
14
|
+
column :id, Integer, :serial => true
|
15
|
+
column :name, String, :nullable => false
|
16
|
+
column :permalink, String
|
17
|
+
column :uri, URI, :nullable => false
|
18
|
+
column :branch, String, :nullable => false, :default => "master"
|
19
|
+
column :command, String, :nullable => false, :default => "rake"
|
20
|
+
column :public, Boolean, :default => true
|
21
|
+
column :building, Boolean, :default => false
|
22
|
+
column :created_at, DateTime
|
23
|
+
column :updated_at, DateTime
|
24
|
+
|
25
|
+
column :build_id, Integer
|
26
|
+
column :notifier_id, Integer
|
27
|
+
end
|
28
|
+
|
29
|
+
create_table :integrity_builds do
|
30
|
+
column :id, Integer, :serial => true
|
31
|
+
column :output, Text, :nullable => false, :default => ""
|
32
|
+
column :successful, Boolean, :nullable => false, :default => false
|
33
|
+
column :commit_identifier, String, :nullable => false
|
34
|
+
column :commit_metadata, Yaml, :nullable => false
|
35
|
+
column :created_at, DateTime
|
36
|
+
column :updated_at, DateTime
|
37
|
+
|
38
|
+
column :project_id, Integer
|
39
|
+
end
|
40
|
+
|
41
|
+
create_table :integrity_notifiers do
|
42
|
+
column :id, Integer, :serial => true
|
43
|
+
column :name, String, :nullable => false
|
44
|
+
column :config, Yaml, :nullable => false
|
45
|
+
|
46
|
+
column :project_id, Integer
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
down do
|
51
|
+
drop_table :integrity_notifiers
|
52
|
+
drop_table :integrity_projects
|
53
|
+
drop_table :integrity_builds
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
migration 2, :add_commits, :verbose => false do
|
58
|
+
up do
|
59
|
+
class ::Integrity::Build
|
60
|
+
property :commit_identifier, String
|
61
|
+
property :commit_metadata, Yaml, :lazy => false
|
62
|
+
property :project_id, Integer
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table :integrity_commits do
|
66
|
+
column :id, Integer, :serial => true
|
67
|
+
column :identifier, String, :nullable => false
|
68
|
+
column :message, String, :nullable => false, :length => 255
|
69
|
+
column :author, String, :nullable => false, :length => 255
|
70
|
+
column :committed_at, DateTime, :nullable => false
|
71
|
+
column :created_at, DateTime
|
72
|
+
column :updated_at, DateTime
|
73
|
+
|
74
|
+
column :project_id, Integer
|
75
|
+
end
|
76
|
+
|
77
|
+
modify_table :integrity_builds do
|
78
|
+
add_column :commit_id, Integer
|
79
|
+
add_column :started_at, DateTime
|
80
|
+
add_column :completed_at, DateTime
|
81
|
+
end
|
82
|
+
|
83
|
+
# Die, orphans, die
|
84
|
+
Build.all(:project_id => nil).destroy!
|
85
|
+
|
86
|
+
# sqlite hodgepockery
|
87
|
+
all_builds = Build.all.each {|b| b.freeze }
|
88
|
+
drop_table :integrity_builds
|
89
|
+
create_table :integrity_builds do
|
90
|
+
column :id, Integer, :serial => true
|
91
|
+
column :started_at, DateTime
|
92
|
+
column :completed_at, DateTime
|
93
|
+
column :successful, Boolean
|
94
|
+
column :output, Text, :nullable => false, :default => ""
|
95
|
+
column :created_at, DateTime
|
96
|
+
column :updated_at, DateTime
|
97
|
+
|
98
|
+
column :commit_id, Integer
|
99
|
+
end
|
100
|
+
|
101
|
+
all_builds.each do |build|
|
102
|
+
commit = Commit.first(:identifier => build.commit_identifier)
|
103
|
+
|
104
|
+
if commit.nil?
|
105
|
+
commit = Commit.create(:identifier => build.commit_identifier,
|
106
|
+
:message => build.commit_metadata[:message],
|
107
|
+
:author => build.commit_metadata[:author],
|
108
|
+
:committed_at => build.commit_metadata[:date],
|
109
|
+
:project_id => build.project_id)
|
110
|
+
end
|
111
|
+
|
112
|
+
Build.create(:commit_id => commit.id,
|
113
|
+
:started_at => build.created_at,
|
114
|
+
:completed_at => build.updated_at,
|
115
|
+
:successful => build.successful,
|
116
|
+
:output => build.output)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
down do
|
121
|
+
modify_table :integrity_builds do
|
122
|
+
add_column :commit_identifier, String, :nullable => false
|
123
|
+
add_column :commit_metadata, Yaml, :nullable => false
|
124
|
+
add_column :project_id, Integer
|
125
|
+
end
|
126
|
+
|
127
|
+
# sqlite hodgepockery
|
128
|
+
all_builds = Build.all.map {|b| b.freeze }
|
129
|
+
drop_table :integrity_builds
|
130
|
+
create_table :integrity_builds do
|
131
|
+
column :id, Integer, :serial => true
|
132
|
+
column :output, Text, :nullable => false, :default => ""
|
133
|
+
column :successful, Boolean, :nullable => false, :default => false
|
134
|
+
column :commit_identifier, String, :nullable => false
|
135
|
+
column :commit_metadata, Yaml, :nullable => false
|
136
|
+
column :created_at, DateTime
|
137
|
+
column :updated_at, DateTime
|
138
|
+
column :project_id, Integer
|
139
|
+
end
|
140
|
+
|
141
|
+
all_builds.each do |build|
|
142
|
+
Build.create(:project_id => build.commit.project_id,
|
143
|
+
:output => build.output,
|
144
|
+
:successful => build.successful,
|
145
|
+
:commit_identifier => build.commit.identifier,
|
146
|
+
:commit_metadata => {
|
147
|
+
:message => build.commit.message,
|
148
|
+
:author => build.commit.author.full,
|
149
|
+
:date => commit.committed_at
|
150
|
+
}.to_yaml)
|
151
|
+
end
|
152
|
+
|
153
|
+
drop_table :commits
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Integrity
|
2
|
+
class Notifier
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :id, Serial
|
6
|
+
property :name, String, :nullable => false
|
7
|
+
property :config, Yaml, :nullable => false, :lazy => false
|
8
|
+
|
9
|
+
belongs_to :project, :class_name => "Integrity::Project"
|
10
|
+
|
11
|
+
validates_is_unique :name, :scope => :project_id
|
12
|
+
validates_present :project_id
|
13
|
+
|
14
|
+
def self.available
|
15
|
+
@available ||= constants.map { |name| const_get(name) }.select { |notifier| valid_notifier?(notifier) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.enable_notifiers(project, enabled, config={})
|
19
|
+
all(:project_id => project).destroy!
|
20
|
+
list_of_enabled_notifiers(enabled).each do |name|
|
21
|
+
create! :project_id => project, :name => name, :config => config[name]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def notify_of_build(build)
|
27
|
+
to_const.notify_of_build(build, config)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def to_const
|
33
|
+
self.class.module_eval(name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.list_of_enabled_notifiers(names)
|
37
|
+
[*names].reject { |n| n.nil? }
|
38
|
+
end
|
39
|
+
private_class_method :list_of_enabled_notifiers
|
40
|
+
|
41
|
+
def self.valid_notifier?(notifier)
|
42
|
+
notifier.respond_to?(:to_haml) && notifier.respond_to?(:notify_of_build) && notifier != Notifier::Base
|
43
|
+
end
|
44
|
+
private_class_method :valid_notifier?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require File.dirname(__FILE__) / "notifier" / "base"
|
49
|
+
|
50
|
+
Dir["#{File.dirname(__FILE__)}/notifier/*.rb"].each &method(:require)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Integrity
|
2
|
+
class Notifier
|
3
|
+
class Base
|
4
|
+
def self.notify_of_build(build, config)
|
5
|
+
Timeout.timeout(8) { new(build, config).deliver! }
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.to_haml
|
9
|
+
raise NoMethodError, "you need to implement this method in your notifier"
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :build
|
13
|
+
|
14
|
+
def initialize(build, config)
|
15
|
+
@build = build
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
def deliver!
|
20
|
+
raise NoMethodError, "you need to implement this method in your notifier"
|
21
|
+
end
|
22
|
+
|
23
|
+
def short_message
|
24
|
+
"Build #{build.short_commit_identifier} #{build.successful? ? "was successful" : "failed"}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def full_message
|
28
|
+
<<-EOM
|
29
|
+
"Build #{build.commit_identifier} #{build.successful? ? "was successful" : "failed"}"
|
30
|
+
|
31
|
+
Commit Message: #{build.commit_message}
|
32
|
+
Commit Date: #{build.commited_at}
|
33
|
+
Commit Author: #{build.commit_author.name}
|
34
|
+
|
35
|
+
Link: #{build_url}
|
36
|
+
|
37
|
+
Build Output:
|
38
|
+
|
39
|
+
#{stripped_build_output}
|
40
|
+
EOM
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_url
|
44
|
+
raise if Integrity.config[:base_uri].nil?
|
45
|
+
Integrity.config[:base_uri] / build.project.permalink / "builds" / build.commit_identifier
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def stripped_build_output
|
51
|
+
build.output.gsub("\e[0m", "").gsub(/\e\[3[1-7]m/, "")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Integrity
|
2
|
+
class Project
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :id, Serial
|
6
|
+
property :name, String, :nullable => false
|
7
|
+
property :permalink, String
|
8
|
+
property :uri, URI, :nullable => false, :length => 255
|
9
|
+
property :branch, String, :nullable => false, :default => "master"
|
10
|
+
property :command, String, :nullable => false, :length => 255, :default => "rake"
|
11
|
+
property :public, Boolean, :default => true
|
12
|
+
property :building, Boolean, :default => false
|
13
|
+
property :created_at, DateTime
|
14
|
+
property :updated_at, DateTime
|
15
|
+
|
16
|
+
has n, :builds, :class_name => "Integrity::Build"
|
17
|
+
has n, :notifiers, :class_name => "Integrity::Notifier"
|
18
|
+
|
19
|
+
before :save, :set_permalink
|
20
|
+
before :destroy, :delete_code
|
21
|
+
|
22
|
+
validates_is_unique :name
|
23
|
+
|
24
|
+
def self.only_public_unless(condition)
|
25
|
+
if condition
|
26
|
+
all
|
27
|
+
else
|
28
|
+
all(:public => true)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def build(commit_identifier="HEAD")
|
33
|
+
return if building?
|
34
|
+
update_attributes(:building => true)
|
35
|
+
ProjectBuilder.new(self).build(commit_identifier)
|
36
|
+
ensure
|
37
|
+
update_attributes(:building => false)
|
38
|
+
send_notifications
|
39
|
+
end
|
40
|
+
|
41
|
+
def push(payload)
|
42
|
+
payload = JSON.parse(payload || "")
|
43
|
+
|
44
|
+
if Integrity.config[:build_all_commits]
|
45
|
+
payload["commits"].sort_by { |commit| Time.parse(commit["timestamp"]) }.each do |commit|
|
46
|
+
build(commit["id"]) if payload["ref"] =~ /#{branch}/
|
47
|
+
end
|
48
|
+
else
|
49
|
+
build(payload["after"]) if payload["ref"] =~ /#{branch}/
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def last_build
|
54
|
+
all_builds.first
|
55
|
+
end
|
56
|
+
|
57
|
+
def previous_builds
|
58
|
+
all_builds.tap {|builds| builds.shift }
|
59
|
+
end
|
60
|
+
|
61
|
+
def status
|
62
|
+
last_build && last_build.status
|
63
|
+
end
|
64
|
+
|
65
|
+
def public=(flag)
|
66
|
+
attribute_set(:public, case flag
|
67
|
+
when "1", "0" then flag == "1"
|
68
|
+
else !!flag
|
69
|
+
end)
|
70
|
+
end
|
71
|
+
|
72
|
+
def config_for(notifier)
|
73
|
+
notifier = notifiers.first(:name => notifier.to_s.split(/::/).last)
|
74
|
+
notifier.blank? ? {} : notifier.config
|
75
|
+
end
|
76
|
+
|
77
|
+
def notifies?(notifier)
|
78
|
+
!notifiers.first(:name => notifier.to_s.split(/::/).last).blank?
|
79
|
+
end
|
80
|
+
|
81
|
+
def enable_notifiers(*args)
|
82
|
+
Notifier.enable_notifiers(id, *args)
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def set_permalink
|
87
|
+
self.permalink = (name || "").downcase.
|
88
|
+
gsub(/'s/, "s").
|
89
|
+
gsub(/&/, "and").
|
90
|
+
gsub(/[^a-z0-9]+/, "-").
|
91
|
+
gsub(/-*$/, "")
|
92
|
+
end
|
93
|
+
|
94
|
+
def delete_code
|
95
|
+
builds.destroy!
|
96
|
+
ProjectBuilder.new(self).delete_code
|
97
|
+
rescue SCM::SCMUnknownError => error
|
98
|
+
Integrity.log "Problem while trying to deleting code: #{error}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def send_notifications
|
102
|
+
notifiers.each do |notifier|
|
103
|
+
begin
|
104
|
+
Integrity.log "Notifying of build #{last_build.short_commit_identifier} using the #{notifier.name} notifier"
|
105
|
+
notifier.notify_of_build last_build
|
106
|
+
rescue Timeout::Error
|
107
|
+
Integrity.log "#{notifier.name} notifier timed out"
|
108
|
+
next
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def all_builds
|
114
|
+
builds.all.sort_by {|b| b.commited_at }.reverse
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|