foca-integrity 0.1.0

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.
@@ -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