alphasights-integrity 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.
Files changed (93) hide show
  1. data/.gitignore +13 -0
  2. data/CHANGES +48 -0
  3. data/README.md +82 -0
  4. data/Rakefile +58 -0
  5. data/bin/integrity +4 -0
  6. data/config/config.sample.ru +21 -0
  7. data/config/config.sample.yml +41 -0
  8. data/config/heroku/.gems +1 -0
  9. data/config/heroku/Rakefile +6 -0
  10. data/config/heroku/config.ru +7 -0
  11. data/config/heroku/integrity-config.rb +14 -0
  12. data/config/thin.sample.yml +13 -0
  13. data/integrity.gemspec +137 -0
  14. data/lib/integrity.rb +77 -0
  15. data/lib/integrity/app.rb +138 -0
  16. data/lib/integrity/author.rb +39 -0
  17. data/lib/integrity/build.rb +52 -0
  18. data/lib/integrity/commit.rb +61 -0
  19. data/lib/integrity/core_ext/object.rb +6 -0
  20. data/lib/integrity/helpers.rb +16 -0
  21. data/lib/integrity/helpers/authorization.rb +33 -0
  22. data/lib/integrity/helpers/breadcrumbs.rb +20 -0
  23. data/lib/integrity/helpers/gravatar.rb +16 -0
  24. data/lib/integrity/helpers/pretty_output.rb +45 -0
  25. data/lib/integrity/helpers/rendering.rb +49 -0
  26. data/lib/integrity/helpers/resources.rb +19 -0
  27. data/lib/integrity/helpers/urls.rb +59 -0
  28. data/lib/integrity/installer.rb +138 -0
  29. data/lib/integrity/migrations.rb +153 -0
  30. data/lib/integrity/notifier.rb +44 -0
  31. data/lib/integrity/notifier/base.rb +74 -0
  32. data/lib/integrity/notifier/test.rb +52 -0
  33. data/lib/integrity/notifier/test/fixtures.rb +108 -0
  34. data/lib/integrity/notifier/test/hpricot_matcher.rb +38 -0
  35. data/lib/integrity/project.rb +94 -0
  36. data/lib/integrity/project/notifiers.rb +31 -0
  37. data/lib/integrity/project/push.rb +43 -0
  38. data/lib/integrity/project_builder.rb +56 -0
  39. data/lib/integrity/scm.rb +19 -0
  40. data/lib/integrity/scm/git.rb +84 -0
  41. data/lib/integrity/scm/git/uri.rb +57 -0
  42. data/public/buttons.css +82 -0
  43. data/public/reset.css +7 -0
  44. data/public/spinner.gif +0 -0
  45. data/test/acceptance/api_test.rb +97 -0
  46. data/test/acceptance/browse_project_builds_test.rb +65 -0
  47. data/test/acceptance/browse_project_test.rb +99 -0
  48. data/test/acceptance/build_notifications_test.rb +114 -0
  49. data/test/acceptance/create_project_test.rb +97 -0
  50. data/test/acceptance/delete_project_test.rb +53 -0
  51. data/test/acceptance/edit_project_test.rb +117 -0
  52. data/test/acceptance/error_page_test.rb +21 -0
  53. data/test/acceptance/installer_test.rb +81 -0
  54. data/test/acceptance/manual_build_project_test.rb +82 -0
  55. data/test/acceptance/not_found_page_test.rb +29 -0
  56. data/test/acceptance/project_syndication_test.rb +30 -0
  57. data/test/acceptance/stylesheet_test.rb +26 -0
  58. data/test/acceptance/unauthorized_page_test.rb +20 -0
  59. data/test/helpers.rb +75 -0
  60. data/test/helpers/acceptance.rb +82 -0
  61. data/test/helpers/acceptance/email_notifier.rb +52 -0
  62. data/test/helpers/acceptance/git_helper.rb +99 -0
  63. data/test/helpers/acceptance/notifier_helper.rb +47 -0
  64. data/test/helpers/acceptance/textfile_notifier.rb +26 -0
  65. data/test/helpers/expectations.rb +4 -0
  66. data/test/helpers/expectations/be_a.rb +23 -0
  67. data/test/helpers/expectations/change.rb +90 -0
  68. data/test/helpers/expectations/have.rb +105 -0
  69. data/test/helpers/expectations/predicates.rb +37 -0
  70. data/test/helpers/initial_migration_fixture.sql +44 -0
  71. data/test/unit/build_test.rb +72 -0
  72. data/test/unit/commit_test.rb +66 -0
  73. data/test/unit/helpers_test.rb +103 -0
  74. data/test/unit/integrity_test.rb +35 -0
  75. data/test/unit/migrations_test.rb +57 -0
  76. data/test/unit/notifier/base_test.rb +43 -0
  77. data/test/unit/notifier_test.rb +96 -0
  78. data/test/unit/project_builder_test.rb +118 -0
  79. data/test/unit/project_test.rb +344 -0
  80. data/test/unit/scm_test.rb +54 -0
  81. data/views/_commit_info.haml +30 -0
  82. data/views/build.haml +2 -0
  83. data/views/error.haml +37 -0
  84. data/views/home.haml +22 -0
  85. data/views/integrity.sass +424 -0
  86. data/views/layout.haml +29 -0
  87. data/views/new.haml +50 -0
  88. data/views/not_found.haml +31 -0
  89. data/views/notifier.haml +7 -0
  90. data/views/project.builder +21 -0
  91. data/views/project.haml +31 -0
  92. data/views/unauthorized.haml +38 -0
  93. metadata +324 -0
@@ -0,0 +1,44 @@
1
+ require File.dirname(__FILE__) + "/notifier/base"
2
+
3
+ module Integrity
4
+ class Notifier
5
+ include DataMapper::Resource
6
+
7
+ property :id, Integer, :serial => true
8
+ property :name, String, :nullable => false
9
+ property :enabled, Boolean, :nullable => false, :default => false
10
+ property :config, Yaml, :nullable => false, :lazy => false
11
+
12
+ belongs_to :project, :class_name => "Integrity::Project",
13
+ :child_key => [:project_id]
14
+
15
+ validates_is_unique :name, :scope => :project_id
16
+ validates_present :project_id
17
+
18
+ def self.available
19
+ @@_notifiers ||= {}
20
+ @@_notifiers
21
+ end
22
+
23
+ def self.register(klass)
24
+ raise ArgumentError unless valid?(klass)
25
+
26
+ available[klass.to_s.split(":").last] = klass
27
+ end
28
+
29
+ def self.valid?(notifier)
30
+ notifier.respond_to?(:to_haml) && notifier.respond_to?(:notify_of_build) &&
31
+ notifier != Notifier::Base
32
+ end
33
+ private_class_method :valid?
34
+
35
+ def notify_of_build(build)
36
+ to_const.notify_of_build(build, config)
37
+ end
38
+
39
+ private
40
+ def to_const
41
+ self.class.available[name]
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,74 @@
1
+ module Integrity
2
+ class Notifier
3
+ class Base
4
+ def self.notify_of_build(build, config)
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
10
+ end
11
+
12
+ def self.to_haml
13
+ raise NotImplementedError, "you need to implement this method in your notifier"
14
+ end
15
+
16
+ attr_reader :commit
17
+
18
+ def initialize(commit, config)
19
+ @commit = commit
20
+ @config = config
21
+ end
22
+
23
+ def build
24
+ warn "Notifier::Base#build is deprecated, use Notifier::Base#commit instead (#{caller[0]})"
25
+ commit
26
+ end
27
+
28
+ def deliver!
29
+ raise NotImplementedError, "you need to implement this method in your notifier"
30
+ end
31
+
32
+ def short_message
33
+ commit.human_readable_status
34
+ end
35
+
36
+ def full_message
37
+ <<-EOM
38
+ "Build #{commit.identifier} #{commit.successful? ? "was successful" : "failed"}"
39
+
40
+ Commit Message: #{commit.message}
41
+ Commit Date: #{commit.committed_at}
42
+ Commit Author: #{commit.author.name}
43
+
44
+ Link: #{commit_url}
45
+
46
+ Build Output:
47
+
48
+ #{stripped_commit_output}
49
+ EOM
50
+ end
51
+
52
+ def commit_url
53
+ raise if Integrity.config[:base_uri].nil?
54
+ Integrity.config[:base_uri] / commit.project.permalink / "commits" / commit.identifier
55
+ end
56
+
57
+ def build_url
58
+ warn "Notifier::Base#build_url is deprecated, use Notifier::Base#commit_url instead (#{caller[0]})"
59
+ commit_url
60
+ end
61
+
62
+ private
63
+
64
+ def stripped_commit_output
65
+ commit.output.gsub("\e[0m", "").gsub(/\e\[3[1-7]m/, "")
66
+ end
67
+
68
+ def stripped_build_output
69
+ warn "Notifier::Base#stripped_build_output is deprecated, use Notifier::base#stripped_commit_output instead (#{caller[0]})"
70
+ stripped_commit_output
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + "/../../integrity"
2
+
3
+ require "integrity/notifier/test/hpricot_matcher"
4
+ require "integrity/notifier/test/fixtures"
5
+
6
+ module Integrity
7
+ class Notifier
8
+ module Test
9
+ def setup_database
10
+ DataMapper.setup(:default, "sqlite3::memory:")
11
+ DataMapper.auto_migrate!
12
+ end
13
+
14
+ def build(state=:successful)
15
+ Integrity::Build.gen(state)
16
+ end
17
+
18
+ def notifier_class
19
+ Integrity::Notifier.const_get(notifier)
20
+ end
21
+
22
+ def provides_option?(option, value=nil)
23
+ selector = "input##{notifier.downcase}_notifier_#{option}"
24
+ selector << "[@name='notifiers[#{notifier}][#{option}]']"
25
+ selector << "[@value='#{value}']" if value
26
+
27
+ form_have_tag?(selector, option => value)
28
+ end
29
+
30
+ def provides_options(*options)
31
+ options.each { |option| assert_form_have_option(option) }
32
+ end
33
+
34
+ private
35
+ def form(config={})
36
+ Haml::Engine.new(notifier_class.to_haml).
37
+ render(OpenStruct.new(:config => config))
38
+ end
39
+
40
+ def form_have_tag?(selector, options={})
41
+ content = options.delete(:content)
42
+ have_tag?(form(options), selector, content)
43
+ end
44
+
45
+ def have_tag?(html, selector, content=nil)
46
+ matcher = HpricotMatcher.new(html)
47
+ assert_equal content, matcher.tag(selector) if content
48
+ matcher.tag(selector)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,108 @@
1
+ require "dm-sweatshop"
2
+
3
+ include DataMapper::Sweatshop::Unique
4
+
5
+ class Array
6
+ def pick
7
+ self[rand(self.length)]
8
+ end
9
+ end
10
+
11
+ def create_notifier!(name)
12
+ klass = Class.new(Integrity::Notifier::Base) do
13
+ def self.to_haml; ""; end
14
+ def deliver!; nil; end
15
+ end
16
+
17
+ unless Integrity::Notifier.const_defined?(name)
18
+ Integrity::Notifier.const_set(name, klass)
19
+ end
20
+ end
21
+
22
+ Integrity::Project.fixture do
23
+ { :name => (name = unique { /\w+/.gen }),
24
+ :uri => "git://github.com/#{/\w+/.gen}/#{name}.git",
25
+ :branch => ["master", "test-refactoring", "lh-34"].pick,
26
+ :command => ["rake", "make", "ant -buildfile test.xml"].pick,
27
+ :public => [true, false].pick,
28
+ :building => [true, false].pick }
29
+ end
30
+
31
+ Integrity::Project.fixture(:integrity) do
32
+ { :name => "Integrity",
33
+ :uri => "git://github.com/foca/integrity.git",
34
+ :branch => "master",
35
+ :command => "rake",
36
+ :public => true,
37
+ :building => false }
38
+ end
39
+
40
+ Integrity::Project.fixture(:my_test_project) do
41
+ { :name => "My Test Project",
42
+ :uri => File.dirname(__FILE__) + "/../../",
43
+ :branch => "master",
44
+ :command => "./test",
45
+ :public => true,
46
+ :building => false }
47
+ end
48
+
49
+ Integrity::Commit.fixture do
50
+ project = Integrity::Project.first || Integrity::Project.gen
51
+
52
+ { :identifier => Digest::SHA1.hexdigest(/[:paragraph:]/.gen),
53
+ :message => /[:sentence:]/.gen,
54
+ :author => /\w+ \w+ <\w+@example.org>/.gen,
55
+ :committed_at => unique {|i| Time.mktime(2008, 12, 15, 18, (59 - i) % 60) },
56
+ :project_id => project.id }
57
+ end
58
+
59
+ Integrity::Commit.fixture(:successful) do
60
+ Integrity::Commit.generate_attributes.update(:build => Integrity::Build.gen(:successful))
61
+ end
62
+
63
+ Integrity::Commit.fixture(:failed) do
64
+ Integrity::Commit.generate_attributes.update(:build => Integrity::Build.gen(:failed))
65
+ end
66
+
67
+ Integrity::Commit.fixture(:pending) do
68
+ Integrity::Commit.generate_attributes.update(:build => Integrity::Build.gen(:pending))
69
+ end
70
+
71
+ Integrity::Build.fixture do
72
+ commit = Integrity::Commit.first || Integrity::Commit.gen
73
+
74
+ { :output => /[:paragraph:]/.gen,
75
+ :successful => true,
76
+ :started_at => unique {|i| Time.mktime(2008, 12, 15, 18, i % 60) },
77
+ :created_at => unique {|i| Time.mktime(2008, 12, 15, 18, i % 60) },
78
+ :completed_at => unique {|i| Time.mktime(2008, 12, 15, 18, i % 60) },
79
+ :commit_id => commit.id }
80
+ end
81
+
82
+ Integrity::Build.fixture(:successful) do
83
+ Integrity::Build.generate_attributes.update(:successful => true)
84
+ end
85
+
86
+ Integrity::Build.fixture(:failed) do
87
+ Integrity::Build.generate_attributes.update(:successful => false)
88
+ end
89
+
90
+ Integrity::Build.fixture(:pending) do
91
+ Integrity::Build.generate_attributes.update(:successful => nil, :started_at => nil, :completed_at => nil)
92
+ end
93
+
94
+ Integrity::Notifier.fixture(:irc) do
95
+ create_notifier! "IRC"
96
+
97
+ { :project => Integrity::Project.generate,
98
+ :name => "IRC",
99
+ :config => { :uri => "irc://irc.freenode.net/integrity" }}
100
+ end
101
+
102
+ Integrity::Notifier.fixture(:twitter) do
103
+ create_notifier! "Twitter"
104
+
105
+ { :project => Integrity::Project.generate,
106
+ :name => "Twitter",
107
+ :config => { :email => "foo@example.org", :pass => "secret" }}
108
+ end
@@ -0,0 +1,38 @@
1
+ require "hpricot"
2
+
3
+ module Integrity
4
+ class Notifier
5
+ module Test
6
+ # Thanks Harry! http://gist.github.com/39960
7
+ class HpricotMatcher
8
+ def initialize(html)
9
+ @doc = Hpricot(html)
10
+ end
11
+
12
+ # elements('h1') returns a Hpricot::Elements object with all h1-tags.
13
+ def elements(selector)
14
+ @doc.search(selector)
15
+ end
16
+
17
+ # element('h1') returns Hpricot::Elem with first h1-tag, or nil if
18
+ # none exist.
19
+ def element(selector)
20
+ @doc.at(selector)
21
+ end
22
+
23
+ # tags('h1') returns the inner HTML of all matched elements mathed.
24
+ def tags(selector)
25
+ e = elements(selector)
26
+ e.map {|x| x.inner_html}
27
+ end
28
+
29
+ # tag('h1') returns the inner HTML of the first mached element, or
30
+ # nil if none matched.
31
+ def tag(selector)
32
+ e = element(selector)
33
+ e && e.inner_html
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,94 @@
1
+ require "integrity/project/notifiers"
2
+ require "integrity/project/push"
3
+
4
+ module Integrity
5
+ class Project
6
+ include DataMapper::Resource
7
+ include Notifiers, Push
8
+
9
+ property :id, Integer, :serial => true
10
+ property :name, String, :nullable => false
11
+ property :permalink, String
12
+ property :uri, URI, :nullable => false, :length => 255
13
+ property :branch, String, :nullable => false, :default => "master"
14
+ property :command, String, :nullable => false, :length => 255, :default => "rake"
15
+ property :public, Boolean, :default => true
16
+ property :building, Boolean, :default => false
17
+ property :created_at, DateTime
18
+ property :updated_at, DateTime
19
+
20
+ has n, :commits, :class_name => "Integrity::Commit",
21
+ :order => [:committed_at.desc]
22
+ has n, :notifiers, :class_name => "Integrity::Notifier"
23
+
24
+ before :save, :set_permalink
25
+ before :destroy, :delete_working_directory
26
+
27
+ validates_is_unique :name
28
+
29
+ def build(commit_identifier="HEAD")
30
+ commit_identifier = head_of_remote_repo if commit_identifier == "HEAD"
31
+ commit = find_or_create_commit_with_identifier(commit_identifier)
32
+
33
+ Build.queue(commit)
34
+ end
35
+
36
+ def last_commit
37
+ commits.first(:project_id => id, :order => [:committed_at.desc])
38
+ end
39
+
40
+ def previous_commits
41
+ commits.all(:project_id => id, :order => [:committed_at.desc]).
42
+ tap {|commits| commits.shift }
43
+ end
44
+
45
+ def status
46
+ last_commit && last_commit.status
47
+ end
48
+
49
+ def human_readable_status
50
+ last_commit && last_commit.human_readable_status
51
+ end
52
+
53
+ def public=(flag)
54
+ attribute_set(:public, case flag
55
+ when "1", "0" then flag == "1"
56
+ else !!flag
57
+ end)
58
+ end
59
+
60
+ private
61
+ def find_or_create_commit_with_identifier(identifier)
62
+ # We abuse +committed_at+ here setting it to Time.now because we use it
63
+ # to sort (for last_commit and previous_commits). I don't like this
64
+ # very much, but for now it's the only solution I can find.
65
+ #
66
+ # This also creates a dependency, as now we *always* have to update the
67
+ # +committed_at+ field after building to ensure the date is correct :(
68
+ #
69
+ # This might also make your commit listings a little jumpy, if some
70
+ # commits change place every time a build finishes =\
71
+ commits.first_or_create({:identifier => identifier, :project_id => id},
72
+ :committed_at => Time.now)
73
+ end
74
+
75
+ def head_of_remote_repo
76
+ SCM.new(uri, branch).head
77
+ end
78
+
79
+ def set_permalink
80
+ attribute_set(:permalink, (name || "").downcase.
81
+ gsub(/'s/, "s").
82
+ gsub(/&/, "and").
83
+ gsub(/[^a-z0-9]+/, "-").
84
+ gsub(/-*$/, ""))
85
+ end
86
+
87
+ def delete_working_directory
88
+ commits.destroy!
89
+ ProjectBuilder.delete_working_directory(self)
90
+ rescue SCM::SCMUnknownError => error
91
+ Integrity.log "Problem while trying to deleting code: #{error}"
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,31 @@
1
+ module Integrity
2
+ class Project
3
+ module Notifiers
4
+ def notifies?(notifier)
5
+ return false unless notifier = notifiers.first(:name => notifier)
6
+
7
+ notifier.enabled?
8
+ end
9
+
10
+ def enabled_notifiers
11
+ notifiers.all(:enabled => true)
12
+ end
13
+
14
+ def config_for(notifier)
15
+ notifier = notifiers.first(:name => notifier)
16
+ notifier ? notifier.config : {}
17
+ end
18
+
19
+ def update_notifiers(to_enable, config)
20
+ config.each_pair { |name, config|
21
+ notifier = notifiers.first(:name => name)
22
+ notifier ||= notifiers.new(:name => name)
23
+
24
+ notifier.enabled = to_enable.include?(name)
25
+ notifier.config = config
26
+ notifier.save
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end