integrity 0.1.8 → 0.1.9.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.
Files changed (77) hide show
  1. data/README.markdown +7 -0
  2. data/Rakefile +77 -124
  3. data/config/config.ru +29 -0
  4. data/config/config.sample.ru +6 -16
  5. data/config/config.sample.yml +15 -12
  6. data/config/config.yml +34 -0
  7. data/lib/integrity.rb +13 -13
  8. data/lib/integrity/app.rb +138 -0
  9. data/lib/integrity/author.rb +39 -0
  10. data/lib/integrity/build.rb +54 -31
  11. data/lib/integrity/commit.rb +71 -0
  12. data/lib/integrity/helpers.rb +3 -3
  13. data/lib/integrity/helpers/authorization.rb +2 -2
  14. data/lib/integrity/helpers/forms.rb +3 -3
  15. data/lib/integrity/helpers/pretty_output.rb +1 -1
  16. data/lib/integrity/helpers/rendering.rb +6 -1
  17. data/lib/integrity/helpers/resources.rb +9 -3
  18. data/lib/integrity/helpers/urls.rb +15 -13
  19. data/lib/integrity/installer.rb +43 -60
  20. data/lib/integrity/migrations.rb +31 -48
  21. data/lib/integrity/notifier.rb +14 -16
  22. data/lib/integrity/notifier/base.rb +29 -19
  23. data/lib/integrity/notifier/test_helpers.rb +100 -0
  24. data/lib/integrity/project.rb +69 -33
  25. data/lib/integrity/project_builder.rb +23 -14
  26. data/lib/integrity/scm/git.rb +15 -14
  27. data/lib/integrity/scm/git/uri.rb +9 -9
  28. data/test/acceptance/api_test.rb +97 -0
  29. data/test/acceptance/browse_project_builds_test.rb +65 -0
  30. data/test/acceptance/browse_project_test.rb +95 -0
  31. data/test/acceptance/build_notifications_test.rb +42 -0
  32. data/test/acceptance/create_project_test.rb +97 -0
  33. data/test/acceptance/delete_project_test.rb +53 -0
  34. data/test/acceptance/edit_project_test.rb +117 -0
  35. data/test/acceptance/error_page_test.rb +18 -0
  36. data/test/acceptance/helpers.rb +2 -0
  37. data/test/acceptance/installer_test.rb +62 -0
  38. data/test/acceptance/manual_build_project_test.rb +82 -0
  39. data/test/acceptance/notifier_test.rb +109 -0
  40. data/test/acceptance/project_syndication_test.rb +30 -0
  41. data/test/acceptance/stylesheet_test.rb +18 -0
  42. data/test/helpers.rb +59 -26
  43. data/test/helpers/acceptance.rb +19 -65
  44. data/test/helpers/acceptance/email_notifier.rb +55 -0
  45. data/test/helpers/acceptance/git_helper.rb +15 -15
  46. data/test/helpers/acceptance/textfile_notifier.rb +3 -3
  47. data/test/helpers/expectations.rb +0 -1
  48. data/test/helpers/expectations/be_a.rb +4 -4
  49. data/test/helpers/expectations/change.rb +5 -5
  50. data/test/helpers/expectations/have.rb +4 -4
  51. data/test/helpers/expectations/predicates.rb +4 -4
  52. data/test/helpers/fixtures.rb +44 -18
  53. data/test/helpers/initial_migration_fixture.sql +44 -0
  54. data/test/unit/build_test.rb +51 -0
  55. data/test/unit/commit_test.rb +83 -0
  56. data/test/unit/helpers_test.rb +56 -0
  57. data/test/unit/integrity_test.rb +18 -0
  58. data/test/unit/migrations_test.rb +56 -0
  59. data/test/unit/notifier_test.rb +123 -0
  60. data/test/unit/project_builder_test.rb +108 -0
  61. data/test/unit/project_test.rb +282 -0
  62. data/test/unit/scm_test.rb +54 -0
  63. data/views/_commit_info.haml +24 -0
  64. data/views/build.haml +2 -2
  65. data/views/error.haml +4 -3
  66. data/views/home.haml +3 -5
  67. data/views/integrity.sass +19 -6
  68. data/views/new.haml +6 -6
  69. data/views/project.builder +9 -9
  70. data/views/project.haml +14 -12
  71. metadata +89 -122
  72. data/VERSION.yml +0 -4
  73. data/app.rb +0 -138
  74. data/integrity.gemspec +0 -76
  75. data/lib/integrity/core_ext/string.rb +0 -5
  76. data/test/helpers/expectations/have_tag.rb +0 -128
  77. data/views/_build_info.haml +0 -18
@@ -0,0 +1,138 @@
1
+ module Integrity
2
+ class App < Sinatra::Default
3
+ set :root, File.dirname(__FILE__) + "/../.."
4
+ set :app_file, __FILE__
5
+ enable :sessions
6
+
7
+ include Integrity
8
+ include Integrity::Helpers
9
+
10
+ not_found do
11
+ status 404
12
+ show :not_found, :title => "lost, are we?"
13
+ end
14
+
15
+ error do
16
+ @error = request.env["sinatra.error"]
17
+ status 500
18
+ show :error, :title => "something has gone terribly wrong"
19
+ end
20
+
21
+ before do
22
+ # The browser only sends http auth data for requests that are explicitly
23
+ # required to do so. This way we get the real values of +#logged_in?+ and
24
+ # +#current_user+
25
+ login_required if session[:user]
26
+ end
27
+
28
+ get "/integrity.css" do
29
+ response["Content-Type"] = "text/css; charset=utf-8"
30
+ etag stylesheet_hash
31
+ sass :integrity
32
+ end
33
+
34
+ get "/" do
35
+ @projects = Project.only_public_unless(authorized?)
36
+ show :home, :title => "projects"
37
+ end
38
+
39
+ get "/login" do
40
+ login_required
41
+
42
+ session[:user] = current_user
43
+ redirect root_url
44
+ end
45
+
46
+ get "/new" do
47
+ login_required
48
+
49
+ @project = Project.new
50
+ show :new, :title => ["projects", "new project"]
51
+ end
52
+
53
+ post "/" do
54
+ login_required
55
+
56
+ @project = Project.new(params[:project_data])
57
+
58
+ if @project.save
59
+ update_notifiers_of(@project)
60
+ redirect project_url(@project)
61
+ else
62
+ show :new, :title => ["projects", "new project"]
63
+ end
64
+ end
65
+
66
+ get "/:project.atom" do
67
+ login_required unless current_project.public?
68
+ response["Content-Type"] = "application/rss+xml; charset=utf-8"
69
+ builder :project
70
+ end
71
+
72
+ get "/:project" do
73
+ login_required unless current_project.public?
74
+ show :project, :title => ["projects", current_project.name]
75
+ end
76
+
77
+ put "/:project" do
78
+ login_required
79
+
80
+ if current_project.update_attributes(params[:project_data])
81
+ update_notifiers_of(current_project)
82
+ redirect project_url(current_project)
83
+ else
84
+ show :new, :title => ["projects", current_project.permalink, "edit"]
85
+ end
86
+ end
87
+
88
+ delete "/:project" do
89
+ login_required
90
+
91
+ current_project.destroy
92
+ redirect root_url
93
+ end
94
+
95
+ get "/:project/edit" do
96
+ login_required
97
+
98
+ show :new, :title => ["projects", current_project.permalink, "edit"]
99
+ end
100
+
101
+ post "/:project/push" do
102
+ login_required
103
+
104
+ content_type "text/plain"
105
+
106
+ begin
107
+ current_project.push(params[:payload])
108
+ 201
109
+ rescue ArgumentError
110
+ [422, "Invalid Request"]
111
+ end
112
+ end
113
+
114
+ post "/:project/builds" do
115
+ login_required
116
+
117
+ current_project.build
118
+ redirect project_url(current_project)
119
+ end
120
+
121
+ get "/:project/commits/:commit" do
122
+ login_required unless current_project.public?
123
+
124
+ show :build, :title => ["projects", current_project.permalink, current_commit.short_commit_identifier]
125
+ end
126
+
127
+ get "/:project/builds/:commit" do
128
+ redirect "/#{params[:project]}/commits/#{params[:commit]}", 301
129
+ end
130
+
131
+ post "/:project/commits/:commit/builds" do
132
+ login_required
133
+
134
+ current_project.build(params[:commit])
135
+ redirect commit_url(current_commit)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,39 @@
1
+ module Integrity
2
+ class Author < DataMapper::Type
3
+ primitive String
4
+ size 65535
5
+ lazy true
6
+
7
+ class AuthorStruct < Struct.new(:name, :email)
8
+ def self.parse(string)
9
+ raise ArgumentError.new("invalid author string") unless string =~ /^(.*) <(.*)>$/
10
+
11
+ new($1.strip, $2.strip)
12
+ end
13
+
14
+ def to_s
15
+ @full ||= "#{name} <#{email}>"
16
+ end
17
+
18
+ alias_method :full, :to_s
19
+ end
20
+
21
+ def self.load(value, property)
22
+ AuthorStruct.parse(value) unless value.nil?
23
+ end
24
+
25
+ def self.dump(value, property)
26
+ return nil if value.nil?
27
+
28
+ value.to_s
29
+ end
30
+
31
+ def self.typecast(value, property)
32
+ case value
33
+ when AuthorStruct then value
34
+ when NilClass then load(nil, property)
35
+ else load(value.to_s, property)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,60 +2,83 @@ module Integrity
2
2
  class Build
3
3
  include DataMapper::Resource
4
4
 
5
- property :id, Serial
6
- property :output, Text, :nullable => false, :default => ""
7
- property :successful, Boolean, :nullable => false, :default => false
8
- property :commit_identifier, String, :nullable => false
9
- property :commit_metadata, Yaml, :nullable => false, :lazy => false
10
- property :created_at, DateTime
11
- property :updated_at, DateTime
5
+ property :id, Integer, :serial => true
6
+ property :output, Text, :default => "", :lazy => false
7
+ property :successful, Boolean, :default => false
8
+ property :commit_id, Integer, :nullable => false
9
+ property :created_at, DateTime
10
+ property :updated_at, DateTime
11
+ property :started_at, DateTime
12
+ property :completed_at, DateTime
12
13
 
13
- belongs_to :project, :class_name => "Integrity::Project"
14
+ belongs_to :commit, :class_name => "Integrity::Commit"
15
+
16
+ def self.pending
17
+ all(:started_at => nil)
18
+ end
19
+
20
+ def pending?
21
+ started_at.nil?
22
+ end
14
23
 
15
24
  def failed?
16
25
  !successful?
17
26
  end
18
27
 
19
28
  def status
20
- successful? ? :success : :failed
29
+ case
30
+ when pending? then :pending
31
+ when successful? then :success
32
+ when failed? then :failed
33
+ end
21
34
  end
22
35
 
23
- def human_readable_status
24
- successful? ? "Build Successful" : "Build Failed"
36
+ def start!(time=Time.now)
37
+ self.started_at = time
25
38
  end
26
39
 
40
+ def complete!(time=Time.now)
41
+ self.completed_at = time
42
+ end
43
+
44
+ #
45
+ # Deprecated methods
46
+ #
27
47
  def short_commit_identifier
28
- sha1?(commit_identifier) ? commit_identifier[0..6] : commit_identifier
48
+ warn "Build#short_commit_identifier is deprecated, use Commit#short_identifier"
49
+ commit.short_identifier
29
50
  end
30
-
31
- def commit_metadata
32
- case data = attribute_get(:commit_metadata)
33
- when String; YAML.load(data)
34
- else data
35
- end
51
+
52
+ def commit_identifier
53
+ warn "Build#commit_identifier is deprecated, use Commit#identifier"
54
+ commit.identifier
36
55
  end
37
56
 
38
57
  def commit_author
39
- @author ||= begin
40
- commit_metadata[:author] =~ /^(.*) <(.*)>$/
41
- OpenStruct.new(:name => $1.strip, :email => $2.strip, :full => commit_metadata[:author])
42
- end
58
+ warn "Build#commit_author is deprecated, use Commit#author"
59
+ commit.author
43
60
  end
44
61
 
45
62
  def commit_message
46
- commit_metadata[:message]
63
+ warn "Build#commit_message is deprecated, use Commit#message"
64
+ commit.message
47
65
  end
48
66
 
49
67
  def commited_at
50
- case commit_metadata[:date]
51
- when String then Time.parse(commit_metadata[:date])
52
- else commit_metadata[:date]
53
- end
68
+ warn "Build#commited_at is deprecated, use Commit#committed_at"
69
+ commit.committed_at
54
70
  end
55
71
 
56
- private
57
- def sha1?(string)
58
- string =~ /^[a-f0-9]{40}$/
59
- end
72
+ def project_id
73
+ warn "Build#project_id is deprecated, use Commit#project_id"
74
+ commit.project_id
75
+ end
76
+
77
+ def commit_metadata
78
+ warn "Build#commit_metadata is deprecated, use the different methods in Commit instead"
79
+ { :message => commit.message,
80
+ :author => commit.author,
81
+ :date => commit.committed_at }
82
+ end
60
83
  end
61
84
  end
@@ -0,0 +1,71 @@
1
+ module Integrity
2
+ class Commit
3
+ include DataMapper::Resource
4
+
5
+ property :id, Integer, :serial => true
6
+ property :identifier, String, :nullable => false
7
+ property :message, String, :length => 255
8
+ property :author, Author, :length => 255
9
+ property :committed_at, DateTime
10
+ property :created_at, DateTime
11
+ property :updated_at, DateTime
12
+
13
+ has 1, :build, :class_name => "Integrity::Build", :order => [:created_at.desc]
14
+ belongs_to :project, :class_name => "Integrity::Project"
15
+
16
+ def message
17
+ attribute_get(:message) || "<Commit message not loaded>"
18
+ end
19
+
20
+ def author
21
+ attribute_get(:author) || Author.load('<Commit author not loaded> <<Commit author not loaded>>', :author)
22
+ end
23
+
24
+ def short_identifier
25
+ identifier.to_s[0..6]
26
+ end
27
+
28
+ def status
29
+ build.nil? ? :pending : build.status
30
+ end
31
+
32
+ def successful?
33
+ status == :success
34
+ end
35
+
36
+ def failed?
37
+ status == :failed
38
+ end
39
+
40
+ def pending?
41
+ status == :pending
42
+ end
43
+
44
+ def human_readable_status
45
+ case status
46
+ when :success; "Built #{short_identifier} successfully"
47
+ when :failed; "Built #{short_identifier} and failed"
48
+ when :pending; "#{short_identifier} hasn't been built yet"
49
+ end
50
+ end
51
+
52
+ def output
53
+ build && build.output
54
+ end
55
+
56
+ def queue_build
57
+ self.build = Build.create(:commit_id => id)
58
+ self.save
59
+
60
+ # Build on foreground (this will move away, I promise)
61
+ ProjectBuilder.new(project).build(self)
62
+ end
63
+
64
+ # Deprecation layer
65
+ alias :short_commit_identifier :short_identifier
66
+ alias :commit_identifier :identifier
67
+ alias :commit_author :author
68
+ alias :commit_message :message
69
+ alias :commited_at :committed_at
70
+ end
71
+ end
@@ -1,7 +1,7 @@
1
1
  Dir["#{File.dirname(__FILE__)}/helpers/*.rb"].each &method(:require)
2
2
 
3
3
  module Integrity
4
- module Helpers
4
+ module Helpers
5
5
  include Authorization
6
6
  include Breadcrumbs
7
7
  include Forms
@@ -9,8 +9,8 @@ module Integrity
9
9
  include Rendering
10
10
  include Resources
11
11
  include Urls
12
-
12
+
13
13
  include Rack::Utils
14
14
  alias :h :escape_html
15
15
  end
16
- end
16
+ end
@@ -1,4 +1,4 @@
1
- require "diddies"
1
+ require "sinatra/ditties"
2
2
 
3
3
  module Integrity
4
4
  module Helpers
@@ -30,4 +30,4 @@ module Integrity
30
30
  end
31
31
  end
32
32
  end
33
- end
33
+ end
@@ -12,8 +12,8 @@ module Integrity
12
12
 
13
13
  def checkbox(name, condition, extras={})
14
14
  attrs = { :name => name, :type => "checkbox", :value => "1" }
15
- attrs.merge!(:checked => condition ? true : nil)
16
- attrs.merge(extras)
15
+ attrs[:checked] = !!condition
16
+ attrs.update(extras)
17
17
  end
18
18
 
19
19
  def notifier_form(notifier)
@@ -25,4 +25,4 @@ module Integrity
25
25
  end
26
26
  end
27
27
  end
28
- end
28
+ end
@@ -42,4 +42,4 @@ module Integrity
42
42
  end
43
43
  end
44
44
  end
45
- end
45
+ end
@@ -1,6 +1,11 @@
1
1
  module Integrity
2
2
  module Helpers
3
3
  module Rendering
4
+ def stylesheet_hash
5
+ @_hash ||= Digest::MD5.file(
6
+ options.views + "/integrity.sass").tap { |file| file.hexdigest }
7
+ end
8
+
4
9
  def show(view, options={})
5
10
  @title = breadcrumbs(*options[:title])
6
11
  haml view
@@ -11,4 +16,4 @@ module Integrity
11
16
  end
12
17
  end
13
18
  end
14
- end
19
+ end
@@ -5,9 +5,15 @@ module Integrity
5
5
  @project ||= Project.first(:permalink => params[:project]) or raise Sinatra::NotFound
6
6
  end
7
7
 
8
- def current_build
9
- @build ||= current_project.builds.first(:commit_identifier => params[:build]) or raise Sinatra::NotFound
8
+ def current_commit
9
+ @commit ||= current_project.commits.first(:identifier => params[:commit]) or raise Sinatra::NotFound
10
+ end
11
+
12
+ def update_notifiers_of(project)
13
+ if params["notifiers"]
14
+ project.enable_notifiers(params["notifiers"].keys, params["notifiers"])
15
+ end
10
16
  end
11
17
  end
12
18
  end
13
- end
19
+ end