foca-integrity 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems"
4
+ require "integrity"
5
+ require "thor"
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
+ after_setup_message
19
+ end
20
+
21
+ private
22
+ attr_reader :root
23
+
24
+ def create_dir_structure
25
+ mkdir_p root
26
+ mkdir_p root / "builds"
27
+ mkdir_p root / "log"
28
+ end
29
+
30
+ def copy_template_files
31
+ cp Integrity.root / "config" / "config.sample.ru", root / "config.ru"
32
+ cp Integrity.root / "config" / "config.sample.yml", root / "config.yml"
33
+ cp Integrity.root / "config" / "thin.sample.yml", root / "thin.yml"
34
+ end
35
+
36
+ def edit_template_files
37
+ config = File.read(root / "config.yml")
38
+ config.gsub!(%r(sqlite3:///var/integrity.db), "sqlite3://#{root}/integrity.db")
39
+ config.gsub!(%r(/path/to/scm/exports), "#{root}/builds")
40
+ File.open(root / "config.yml", "w") {|f| f.puts config }
41
+ end
42
+
43
+ def after_setup_message
44
+ puts
45
+ puts %Q(Awesome! Integrity was installed successfully!)
46
+ puts
47
+ puts %Q(If you want to enable notifiers, install the gems and then require them)
48
+ puts %Q(in #{root}/config.ru)
49
+ puts
50
+ puts %Q(For example:)
51
+ puts
52
+ puts %Q( sudo gem install -s http://gems.github.com foca-integrity-email)
53
+ puts
54
+ puts %Q(And then in #{root}/config.ru add:)
55
+ puts
56
+ puts %Q( require "notifier/email")
57
+ end
58
+ end
59
+
60
+ WithIntegrity.start
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ require "lib/integrity"
3
+ require "sinatra"
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
+
23
+ Sinatra::Application.default_options.merge!(
24
+ :run => false,
25
+ :port => Integrity.config[:port],
26
+ :env => :production
27
+ )
28
+
29
+ require "app"
30
+ run Sinatra.application
@@ -0,0 +1,8 @@
1
+ :base_uri: http://integrity.domain.tld
2
+ :database_uri: sqlite3:///var/integrity.db
3
+ :export_directory: /path/to/scm/exports
4
+ :hash_admin_password: true
5
+ :use_basic_auth: true
6
+ :admin_username: username
7
+ :admin_password: a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
8
+ :port: 4567
@@ -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
@@ -0,0 +1,65 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'integrity'
3
+ s.version = '0.1.0'
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.files = %w(
14
+ README.markdown
15
+ Rakefile
16
+ app.rb
17
+ bin/integrity
18
+ config/config.sample.ru
19
+ config/config.sample.yml
20
+ config/thin.sample.yml
21
+ integrity.gemspec
22
+ lib/integrity.rb
23
+ lib/integrity/build.rb
24
+ lib/integrity/builder.rb
25
+ lib/integrity/core_ext/object.rb
26
+ lib/integrity/core_ext/string.rb
27
+ lib/integrity/core_ext/time.rb
28
+ lib/integrity/notifier.rb
29
+ lib/integrity/notifier/base.rb
30
+ lib/integrity/project.rb
31
+ lib/integrity/scm.rb
32
+ lib/integrity/scm/git.rb
33
+ lib/integrity/scm/git/uri.rb
34
+ lib/integrity/version.rb
35
+ public/buttons.css
36
+ public/reset.css
37
+ public/spinner.gif
38
+ vendor/sinatra-hacks/lib/hacks.rb
39
+ views/build.haml
40
+ views/build_info.haml
41
+ views/home.haml
42
+ views/integrity.sass
43
+ views/layout.haml
44
+ views/new.haml
45
+ views/not_found.haml
46
+ views/notifier.haml
47
+ views/project.haml
48
+ views/unauthorized.haml
49
+ spec/spec_helper.rb
50
+ spec/form_field_matchers.rb
51
+ )
52
+
53
+ s.add_dependency 'sinatra', ['>= 0.3.2']
54
+ s.add_dependency 'dm-core', ['>= 0.9.5']
55
+ s.add_dependency 'dm-validations', ['>= 0.9.5']
56
+ s.add_dependency 'dm-types', ['>= 0.9.5']
57
+ s.add_dependency 'dm-timestamps', ['>= 0.9.5']
58
+ s.add_dependency 'dm-aggregates', ['>= 0.9.5']
59
+ s.add_dependency 'data_objects', ['>= 0.9.5']
60
+ s.add_dependency 'do_sqlite3', ['>= 0.9.5']
61
+ s.add_dependency 'json'
62
+ s.add_dependency 'foca-sinatra-diddies', ['>= 0.0.2']
63
+ s.add_dependency 'rspec_hpricot_matchers'
64
+ s.add_dependency 'thor'
65
+ end
@@ -0,0 +1,47 @@
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:4567',
34
+ :use_basic_auth => false,
35
+ :port => 9876 }
36
+ end
37
+
38
+ def self.config
39
+ @config ||= default_configuration
40
+ end
41
+
42
+ def self.config=(file)
43
+ @config = default_configuration.merge(YAML.load_file(file))
44
+ rescue Errno::ENOENT
45
+ {}
46
+ end
47
+ 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,6 @@
1
+ class Object
2
+ def tap
3
+ yield self
4
+ self
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def /(other)
3
+ File.join(self, other)
4
+ end
5
+ 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
+ all.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)
@@ -0,0 +1,56 @@
1
+ module Integrity
2
+ class Notifier
3
+ class Base
4
+ def self.notify_of_build(build, config)
5
+ new(build, config).deliver!
6
+ end
7
+
8
+ def self.to_haml
9
+ filename = name.split("::").last.downcase
10
+ File.read File.join(Integrity.root / "lib" / "integrity" / "notifier" / "#{filename}.haml")
11
+ end
12
+
13
+ attr_reader :build
14
+
15
+ def initialize(build, config)
16
+ @build = build
17
+ @config = config
18
+ end
19
+
20
+ def deliver!
21
+ raise NoMethodError, "you need to implement this method in your notifier"
22
+ end
23
+
24
+ def short_message
25
+ "Build #{build.short_commit_identifier} #{build.successful? ? "was successful" : "failed"}"
26
+ end
27
+
28
+ def full_message
29
+ <<-EOM
30
+ "Build #{build.commit_identifier} #{build.successful? ? "was successful" : "failed"}"
31
+
32
+ Commit Message: #{build.commit_message}
33
+ Commit Date: #{build.commited_at}
34
+ Commit Author: #{build.commit_author.name}
35
+
36
+ Link: #{build_url}
37
+
38
+ Build Output:
39
+
40
+ #{stripped_build_output}
41
+ EOM
42
+ end
43
+
44
+ def build_url
45
+ raise if Integrity.config[:base_uri].nil?
46
+ Integrity.config[:base_uri] / build.project.permalink / "builds" / build.commit_identifier
47
+ end
48
+
49
+ private
50
+
51
+ def stripped_build_output
52
+ build.output.gsub("\e[0m", '').gsub(/\e\[3[1-7]m/, '')
53
+ end
54
+ end
55
+ end
56
+ end