defunkt-integrity 0.1.1
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 +146 -0
- data/Rakefile +89 -0
- data/app.rb +249 -0
- data/bin/integrity +80 -0
- data/config/config.sample.ru +30 -0
- data/config/config.sample.yml +31 -0
- data/config/thin.sample.yml +14 -0
- data/integrity.gemspec +66 -0
- data/lib/integrity.rb +44 -0
- data/lib/integrity/build.rb +56 -0
- data/lib/integrity/builder.rb +42 -0
- data/lib/integrity/core_ext/object.rb +6 -0
- data/lib/integrity/core_ext/string.rb +5 -0
- data/lib/integrity/core_ext/time.rb +13 -0
- data/lib/integrity/notifier.rb +49 -0
- data/lib/integrity/notifier/base.rb +55 -0
- data/lib/integrity/project.rb +87 -0
- data/lib/integrity/scm.rb +22 -0
- data/lib/integrity/scm/git.rb +72 -0
- data/lib/integrity/scm/git/uri.rb +57 -0
- data/lib/integrity/version.rb +3 -0
- data/public/buttons.css +82 -0
- data/public/reset.css +7 -0
- data/public/spinner.gif +0 -0
- data/spec/form_field_matchers.rb +91 -0
- data/spec/spec_helper.rb +130 -0
- data/vendor/sinatra-hacks/lib/hacks.rb +49 -0
- data/views/build.haml +2 -0
- data/views/build_info.haml +22 -0
- data/views/home.haml +22 -0
- data/views/integrity.sass +391 -0
- data/views/layout.haml +25 -0
- data/views/new.haml +53 -0
- data/views/not_found.haml +31 -0
- data/views/notifier.haml +7 -0
- data/views/project.haml +36 -0
- data/views/unauthorized.haml +38 -0
- metadata +197 -0
data/bin/integrity
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "rubygems"
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + "/../lib/integrity"
|
6
|
+
|
7
|
+
class WithIntegrity < Thor
|
8
|
+
include FileUtils
|
9
|
+
|
10
|
+
desc "Install integrity at PATH",
|
11
|
+
"Copy template files at PATH. After this, edit the files to your convenience"
|
12
|
+
def install(path)
|
13
|
+
@root = File.expand_path(path)
|
14
|
+
|
15
|
+
create_dir_structure
|
16
|
+
copy_template_files
|
17
|
+
edit_template_files
|
18
|
+
create_db(root / 'config.yml')
|
19
|
+
after_setup_message
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Create databases for CONFIG",
|
23
|
+
"Create the database necessary to run Integrity"
|
24
|
+
def create_db(config)
|
25
|
+
Integrity.config = config
|
26
|
+
Integrity.new
|
27
|
+
DataMapper.auto_migrate!
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :root
|
32
|
+
|
33
|
+
def create_dir_structure
|
34
|
+
mkdir_p root
|
35
|
+
mkdir_p root / "builds"
|
36
|
+
mkdir_p root / "log"
|
37
|
+
end
|
38
|
+
|
39
|
+
def copy_template_files
|
40
|
+
cp Integrity.root / "config" / "config.sample.ru", root / "config.ru"
|
41
|
+
cp Integrity.root / "config" / "config.sample.yml", root / "config.yml"
|
42
|
+
cp Integrity.root / "config" / "thin.sample.yml", root / "thin.yml"
|
43
|
+
end
|
44
|
+
|
45
|
+
def edit_template_files
|
46
|
+
edit_integrity_configuration
|
47
|
+
edit_thin_configuration
|
48
|
+
end
|
49
|
+
|
50
|
+
def edit_integrity_configuration
|
51
|
+
config = File.read(root / "config.yml")
|
52
|
+
config.gsub!(%r(sqlite3:///var/integrity.db), "sqlite3://#{root}/integrity.db")
|
53
|
+
config.gsub!(%r(/path/to/scm/exports), "#{root}/builds")
|
54
|
+
File.open(root / "config.yml", "w") {|f| f.puts config }
|
55
|
+
end
|
56
|
+
|
57
|
+
def edit_thin_configuration
|
58
|
+
config = File.read(root / 'thin.yml')
|
59
|
+
config.gsub!(%r(/apps/integrity), root)
|
60
|
+
File.open(root / 'thin.yml', 'w') { |f| f.puts config }
|
61
|
+
end
|
62
|
+
|
63
|
+
def after_setup_message
|
64
|
+
puts
|
65
|
+
puts %Q(Awesome! Integrity was installed successfully!)
|
66
|
+
puts
|
67
|
+
puts %Q(If you want to enable notifiers, install the gems and then require them)
|
68
|
+
puts %Q(in #{root}/config.ru)
|
69
|
+
puts
|
70
|
+
puts %Q(For example:)
|
71
|
+
puts
|
72
|
+
puts %Q( sudo gem install -s http://gems.github.com foca-integrity-email)
|
73
|
+
puts
|
74
|
+
puts %Q(And then in #{root}/config.ru add:)
|
75
|
+
puts
|
76
|
+
puts %Q( require "notifier/email")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
WithIntegrity.start
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'integrity'
|
4
|
+
|
5
|
+
# If you want to add any notifiers, install the gems and then require them here
|
6
|
+
# For example, to enable the Email notifier: install the gem (from github:
|
7
|
+
#
|
8
|
+
# sudo gem install -s http://gems.github.com foca-integrity-email
|
9
|
+
#
|
10
|
+
# And then uncomment the following line:
|
11
|
+
#
|
12
|
+
# require "notifier/email"
|
13
|
+
|
14
|
+
# Load integrity's configuration.
|
15
|
+
Integrity.config = File.expand_path('./config.yml')
|
16
|
+
|
17
|
+
#######################################################################
|
18
|
+
## ##
|
19
|
+
## == DON'T EDIT ANYTHING BELOW UNLESS YOU KNOW WHAT YOU'RE DOING == ##
|
20
|
+
## ##
|
21
|
+
#######################################################################
|
22
|
+
require Integrity.root / 'app'
|
23
|
+
|
24
|
+
set :public, Integrity.root / 'public'
|
25
|
+
set :views, Integrity.root / 'views'
|
26
|
+
set :port, 8910
|
27
|
+
set :env, :production
|
28
|
+
disable :run, :reload
|
29
|
+
|
30
|
+
run Sinatra.application
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Domain where integrity will be running from. This is used to have
|
2
|
+
# nice URLs in your notifications.
|
3
|
+
# For example:
|
4
|
+
# http://builder.integrityapp.com
|
5
|
+
:base_uri: http://integrity.domain.tld
|
6
|
+
|
7
|
+
# This should be a complete connection string to your database. For example
|
8
|
+
# `mysql://user@localhost/integrity` (you need an `integrity` db created in
|
9
|
+
# localhost, of course).
|
10
|
+
:database_uri: sqlite3:///var/integrity.db
|
11
|
+
|
12
|
+
# This is where your project's code will be checked out to. Make sure it's
|
13
|
+
# writable by the user that runs Integrity.
|
14
|
+
:export_directory: /path/to/scm/exports
|
15
|
+
|
16
|
+
# Enable or disable HTTP authentication for the app. BE AWARE that if you
|
17
|
+
# disable this anyone can delete and alter projects, so do it only if your
|
18
|
+
# app is running in a controlled environment (ie, behind your company's
|
19
|
+
# firewall.)
|
20
|
+
:use_basic_auth: true
|
21
|
+
|
22
|
+
# When `use_basic_auth` is true, the admin's username for HTTP authentication.
|
23
|
+
:admin_username: username
|
24
|
+
|
25
|
+
# When `use_basic_auth` is true, the admin's password. Usually saved as a
|
26
|
+
# SHA1 hash. See the next option.
|
27
|
+
:admin_password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
|
28
|
+
|
29
|
+
# If this is true, then whenever we authenticate the admin user, will hash
|
30
|
+
# it using SHA1. If not, we'll assume the provided password is in plain text.
|
31
|
+
:hash_admin_password: true
|
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
environment: production
|
3
|
+
chdir: /apps/integrity
|
4
|
+
address: 127.0.0.1
|
5
|
+
user: deploy
|
6
|
+
group: deploy
|
7
|
+
port: 8910
|
8
|
+
pid: /apps/integrity/thin.pid
|
9
|
+
rackup: /apps/integrity/config.ru
|
10
|
+
log: /apps/integrity/log/thin.log
|
11
|
+
max_conns: 1024
|
12
|
+
timeout: 30
|
13
|
+
max_persistent_conns: 512
|
14
|
+
daemonize: true
|
data/integrity.gemspec
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'integrity'
|
3
|
+
s.version = '0.1.1'
|
4
|
+
s.date = '2008-11-14'
|
5
|
+
s.summary = 'The easy and fun Continuous Integration server'
|
6
|
+
s.description = 'Your Friendly Continuous Integration server. Easy, fun and painless!'
|
7
|
+
s.homepage = 'http://integrityapp.com'
|
8
|
+
s.rubyforge_project = 'integrity'
|
9
|
+
s.email = 'contacto@nicolassanguinetti.info'
|
10
|
+
s.authors = ['Nicolás Sanguinetti', 'Simon Rozet']
|
11
|
+
s.has_rdoc = false
|
12
|
+
s.executables = ['integrity']
|
13
|
+
s.post_install_message = 'Run `integrity help` for information on how to setup Integrity.'
|
14
|
+
s.files = %w(
|
15
|
+
README.markdown
|
16
|
+
Rakefile
|
17
|
+
app.rb
|
18
|
+
bin/integrity
|
19
|
+
config/config.sample.ru
|
20
|
+
config/config.sample.yml
|
21
|
+
config/thin.sample.yml
|
22
|
+
integrity.gemspec
|
23
|
+
lib/integrity.rb
|
24
|
+
lib/integrity/build.rb
|
25
|
+
lib/integrity/builder.rb
|
26
|
+
lib/integrity/core_ext/object.rb
|
27
|
+
lib/integrity/core_ext/string.rb
|
28
|
+
lib/integrity/core_ext/time.rb
|
29
|
+
lib/integrity/notifier.rb
|
30
|
+
lib/integrity/notifier/base.rb
|
31
|
+
lib/integrity/project.rb
|
32
|
+
lib/integrity/scm.rb
|
33
|
+
lib/integrity/scm/git.rb
|
34
|
+
lib/integrity/scm/git/uri.rb
|
35
|
+
lib/integrity/version.rb
|
36
|
+
public/buttons.css
|
37
|
+
public/reset.css
|
38
|
+
public/spinner.gif
|
39
|
+
vendor/sinatra-hacks/lib/hacks.rb
|
40
|
+
views/build.haml
|
41
|
+
views/build_info.haml
|
42
|
+
views/home.haml
|
43
|
+
views/integrity.sass
|
44
|
+
views/layout.haml
|
45
|
+
views/new.haml
|
46
|
+
views/not_found.haml
|
47
|
+
views/notifier.haml
|
48
|
+
views/project.haml
|
49
|
+
views/unauthorized.haml
|
50
|
+
spec/spec_helper.rb
|
51
|
+
spec/form_field_matchers.rb
|
52
|
+
)
|
53
|
+
|
54
|
+
s.add_dependency 'sinatra', ['>= 0.3.2']
|
55
|
+
s.add_dependency 'dm-core', ['>= 0.9.5']
|
56
|
+
s.add_dependency 'dm-validations', ['>= 0.9.5']
|
57
|
+
s.add_dependency 'dm-types', ['>= 0.9.5']
|
58
|
+
s.add_dependency 'dm-timestamps', ['>= 0.9.5']
|
59
|
+
s.add_dependency 'dm-aggregates', ['>= 0.9.5']
|
60
|
+
s.add_dependency 'data_objects', ['>= 0.9.5']
|
61
|
+
s.add_dependency 'do_sqlite3', ['>= 0.9.5']
|
62
|
+
s.add_dependency 'json'
|
63
|
+
s.add_dependency 'foca-sinatra-diddies', ['>= 0.0.2']
|
64
|
+
s.add_dependency 'rspec_hpricot_matchers'
|
65
|
+
s.add_dependency 'thor'
|
66
|
+
end
|
data/lib/integrity.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
__DIR__ = File.dirname(__FILE__)
|
2
|
+
$:.unshift "#{__DIR__}/integrity", *Dir["#{__DIR__}/../vendor/**/lib"].to_a
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'json'
|
6
|
+
require 'dm-core'
|
7
|
+
require 'dm-validations'
|
8
|
+
require 'dm-types'
|
9
|
+
require 'dm-timestamps'
|
10
|
+
require 'dm-aggregates'
|
11
|
+
|
12
|
+
require 'yaml'
|
13
|
+
require 'digest/sha1'
|
14
|
+
|
15
|
+
require "core_ext/object"
|
16
|
+
require "core_ext/string"
|
17
|
+
require "core_ext/time"
|
18
|
+
|
19
|
+
%w(project build builder scm scm/git notifier version).each &method(:require)
|
20
|
+
|
21
|
+
module Integrity
|
22
|
+
def self.new
|
23
|
+
DataMapper.setup(:default, config[:database_uri])
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.root
|
27
|
+
File.expand_path(File.join(File.dirname(__FILE__), ".."))
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.default_configuration
|
31
|
+
@defaults ||= { :database_uri => 'sqlite3::memory:',
|
32
|
+
:export_directory => root / 'exports',
|
33
|
+
:base_uri => 'http://localhost:8910',
|
34
|
+
:use_basic_auth => false }
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.config
|
38
|
+
@config ||= default_configuration
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.config=(file)
|
42
|
+
@config = default_configuration.merge(YAML.load_file(file))
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Integrity
|
4
|
+
class Build
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
property :id, Integer, :serial => true
|
8
|
+
property :output, Text, :nullable => false, :default => ''
|
9
|
+
property :successful, Boolean, :nullable => false, :default => false
|
10
|
+
property :commit_identifier, String, :nullable => false
|
11
|
+
property :commit_metadata, Yaml, :nullable => false, :lazy => false
|
12
|
+
property :created_at, DateTime
|
13
|
+
property :updated_at, DateTime
|
14
|
+
|
15
|
+
belongs_to :project, :class_name => "Integrity::Project"
|
16
|
+
|
17
|
+
def failed?
|
18
|
+
!successful?
|
19
|
+
end
|
20
|
+
|
21
|
+
def status
|
22
|
+
successful? ? :success : :failed
|
23
|
+
end
|
24
|
+
|
25
|
+
def human_readable_status
|
26
|
+
successful? ? 'Build Successful' : 'Build Failed'
|
27
|
+
end
|
28
|
+
|
29
|
+
def short_commit_identifier
|
30
|
+
sha1?(commit_identifier) ? commit_identifier[0..6] : commit_identifier
|
31
|
+
end
|
32
|
+
|
33
|
+
def commit_author
|
34
|
+
@author ||= begin
|
35
|
+
commit_metadata[:author] =~ /^(.*) <(.*)>$/
|
36
|
+
OpenStruct.new(:name => $1.strip, :email => $2.strip, :full => commit_metadata[:author])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def commit_message
|
41
|
+
commit_metadata[:message]
|
42
|
+
end
|
43
|
+
|
44
|
+
def commited_at
|
45
|
+
case commit_metadata[:date]
|
46
|
+
when String then Time.parse(commit_metadata[:date])
|
47
|
+
else commit_metadata[:date]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def sha1?(string)
|
53
|
+
string =~ /^[a-f0-9]{40}$/
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Integrity
|
4
|
+
class Builder
|
5
|
+
attr_reader :build_script
|
6
|
+
|
7
|
+
def initialize(project)
|
8
|
+
@uri = project.uri
|
9
|
+
@build_script = project.command
|
10
|
+
@branch = project.branch
|
11
|
+
@scm = SCM.new(@uri, @branch, export_directory)
|
12
|
+
@build = Build.new(:project => project)
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(commit)
|
16
|
+
@scm.with_revision(commit) { run_build_script }
|
17
|
+
@build
|
18
|
+
ensure
|
19
|
+
@build.commit_identifier = @scm.commit_identifier(commit)
|
20
|
+
@build.commit_metadata = @scm.commit_metadata(commit)
|
21
|
+
@build.save
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete_code
|
25
|
+
FileUtils.rm_r export_directory
|
26
|
+
rescue Errno::ENOENT
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def export_directory
|
32
|
+
Integrity.config[:export_directory] / "#{SCM.working_tree_path(@uri)}-#{@branch}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_build_script
|
36
|
+
IO.popen "(cd #{@scm.working_directory} && #{build_script}) 2>&1", "r" do |pipe|
|
37
|
+
@build.output = pipe.read
|
38
|
+
end
|
39
|
+
@build.successful = $?.success?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Time
|
2
|
+
alias :strftime_without_ordinals :strftime
|
3
|
+
def strftime(format_string)
|
4
|
+
format_string.gsub! "%o", case day
|
5
|
+
when 1, 21, 31 then "st"
|
6
|
+
when 2, 22 then "nd"
|
7
|
+
when 3, 23 then "rd"
|
8
|
+
else "th"
|
9
|
+
end
|
10
|
+
|
11
|
+
strftime_without_ordinals(format_string)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Integrity
|
2
|
+
class Notifier
|
3
|
+
include DataMapper::Resource
|
4
|
+
|
5
|
+
property :id, Integer, :serial => true
|
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 do |notifier|
|
16
|
+
notifier.respond_to?(:to_haml) && notifier.respond_to?(:notify_of_build)
|
17
|
+
end - [Notifier::Base]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.enable_notifiers(project, enabled, config={})
|
21
|
+
Project.get(project).notifiers.destroy!
|
22
|
+
list_of_enabled_notifiers(enabled).each do |name|
|
23
|
+
create! :project_id => project, :name => name, :config => config[name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify_of_build(build)
|
28
|
+
to_const.notify_of_build(build, config)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def to_const
|
34
|
+
self.class.module_eval(name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.list_of_enabled_notifiers(names)
|
38
|
+
case names
|
39
|
+
when Array then names
|
40
|
+
when NilClass then []
|
41
|
+
else [names]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
require File.dirname(__FILE__) / 'notifier' / 'base'
|
48
|
+
|
49
|
+
Dir["#{File.dirname(__FILE__)}/notifier/*.rb"].each &method(:require)
|