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
data/lib/integrity.rb ADDED
@@ -0,0 +1,77 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require "json"
4
+ require "haml"
5
+ require "dm-core"
6
+ require "dm-validations"
7
+ require "dm-types"
8
+ require "dm-timestamps"
9
+ require "dm-aggregates"
10
+ require "sinatra/base"
11
+
12
+ require "yaml"
13
+ require "logger"
14
+ require "digest/sha1"
15
+ require "timeout"
16
+ require "ostruct"
17
+ require "pathname"
18
+
19
+ require "integrity/core_ext/object"
20
+
21
+ require "integrity/project"
22
+ require "integrity/author"
23
+ require "integrity/commit"
24
+ require "integrity/build"
25
+ require "integrity/project_builder"
26
+ require "integrity/scm"
27
+ require "integrity/scm/git"
28
+ require "integrity/notifier"
29
+ require "integrity/helpers"
30
+ require "integrity/app"
31
+
32
+ module Integrity
33
+ def self.new(config=nil)
34
+ if config.is_a?(String) && File.file?(config)
35
+ self.config = YAML.load_file(config)
36
+ elsif config.is_a?(Hash)
37
+ self.config = config
38
+ end
39
+
40
+ DataMapper.setup(:default, self.config[:database_uri])
41
+ end
42
+
43
+ def self.default_configuration
44
+ @defaults ||= { :database_uri => "sqlite3::memory:",
45
+ :export_directory => "/tmp/exports",
46
+ :log => STDOUT,
47
+ :base_uri => "http://localhost:8910",
48
+ :use_basic_auth => false,
49
+ :build_all_commits => true,
50
+ :log_debug_info => false }
51
+ end
52
+
53
+ def self.config
54
+ @config ||= default_configuration.dup
55
+ end
56
+
57
+ def self.config=(options)
58
+ @config = default_configuration.merge(options)
59
+ end
60
+
61
+ def self.log(message, &block)
62
+ logger.info(message, &block)
63
+ end
64
+
65
+ def self.logger
66
+ @logger ||= Logger.new(config[:log], "daily").tap do |logger|
67
+ logger.formatter = LogFormatter.new
68
+ end
69
+ end
70
+ private_class_method :logger
71
+
72
+ class LogFormatter < Logger::Formatter
73
+ def call(severity, time, progname, msg)
74
+ time.strftime("[%H:%M:%S] ") + msg2str(msg) + "\n"
75
+ end
76
+ end
77
+ end
@@ -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 = authorized? ? Project.all : Project.all(:public => true)
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.to_s
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).to_s
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).to_s
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.to_s
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).to_s
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_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).to_s
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
@@ -0,0 +1,52 @@
1
+ module Integrity
2
+ class Build
3
+ include DataMapper::Resource
4
+
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
13
+
14
+ belongs_to :commit, :class_name => "Integrity::Commit",
15
+ :child_key => [:commit_id]
16
+
17
+ def self.pending
18
+ all(:started_at => nil)
19
+ end
20
+
21
+ def self.queue(commit)
22
+ commit.update_attributes(:build => new)
23
+
24
+ # Build on foreground (this will move away, I promise)
25
+ ProjectBuilder.build(commit)
26
+ end
27
+
28
+ def pending?
29
+ started_at.nil?
30
+ end
31
+
32
+ def failed?
33
+ !successful?
34
+ end
35
+
36
+ def status
37
+ case
38
+ when pending? then :pending
39
+ when successful? then :success
40
+ when failed? then :failed
41
+ end
42
+ end
43
+
44
+ def start!(time=Time.now)
45
+ self.started_at = time
46
+ end
47
+
48
+ def complete!(time=Time.now)
49
+ self.completed_at = time
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,61 @@
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 :url, String
10
+ property :committed_at, DateTime
11
+ property :created_at, DateTime
12
+ property :updated_at, DateTime
13
+
14
+ has 1, :build, :class_name => "Integrity::Build",
15
+ :order => [:created_at.desc]
16
+
17
+ belongs_to :project, :class_name => "Integrity::Project",
18
+ :child_key => [:project_id]
19
+
20
+ def message
21
+ attribute_get(:message) || "<Commit message not loaded>"
22
+ end
23
+
24
+ def author
25
+ attribute_get(:author) ||
26
+ Author.load('<Commit author not loaded> <<Commit author not loaded>>', :author)
27
+ end
28
+
29
+ def short_identifier
30
+ identifier.to_s[0..6]
31
+ end
32
+
33
+ def status
34
+ build.nil? ? :pending : build.status
35
+ end
36
+
37
+ def successful?
38
+ status == :success
39
+ end
40
+
41
+ def failed?
42
+ status == :failed
43
+ end
44
+
45
+ def pending?
46
+ status == :pending
47
+ end
48
+
49
+ def human_readable_status
50
+ case status
51
+ when :success; "Built #{short_identifier} successfully"
52
+ when :failed; "Built #{short_identifier} and failed"
53
+ when :pending; "#{short_identifier} hasn't been built yet"
54
+ end
55
+ end
56
+
57
+ def output
58
+ build && build.output
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,6 @@
1
+ class Object
2
+ def tap
3
+ yield self
4
+ self
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ Dir["#{File.dirname(__FILE__)}/helpers/*.rb"].each &method(:require)
2
+
3
+ module Integrity
4
+ module Helpers
5
+ include Authorization
6
+ include Breadcrumbs
7
+ include PrettyOutput
8
+ include Rendering
9
+ include Resources
10
+ include Urls
11
+ include Gravatar
12
+
13
+ include Rack::Utils
14
+ alias :h :escape_html
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ require "sinatra/authorization"
2
+
3
+ module Integrity
4
+ module Helpers
5
+ module Authorization
6
+ include Sinatra::Authorization
7
+
8
+ def authorization_realm
9
+ "Integrity"
10
+ end
11
+
12
+ def authorized?
13
+ return true unless Integrity.config[:use_basic_auth]
14
+ !!request.env["REMOTE_USER"]
15
+ end
16
+
17
+ def authorize(user, password)
18
+ if Integrity.config[:hash_admin_password]
19
+ password = Digest::SHA1.hexdigest(password)
20
+ end
21
+
22
+ !Integrity.config[:use_basic_auth] ||
23
+ (Integrity.config[:admin_username] == user &&
24
+ Integrity.config[:admin_password] == password)
25
+ end
26
+
27
+ def unauthorized!(realm=authorization_realm)
28
+ response["WWW-Authenticate"] = %(Basic realm="#{realm}")
29
+ throw :halt, [401, show(:unauthorized, :title => "incorrect credentials")]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ module Integrity
2
+ module Helpers
3
+ module Breadcrumbs
4
+ def pages
5
+ @pages ||= [["projects", root_path("/")], ["new project", root_path("/new")]]
6
+ end
7
+
8
+ def breadcrumbs(*crumbs)
9
+ crumbs[0..-2].map do |crumb|
10
+ if page_data = pages.detect {|c| c.first == crumb }
11
+ %Q(<a href="#{page_data.last}">#{page_data.first}</a>)
12
+ elsif @project && @project.permalink == crumb
13
+ %Q(<a href="#{project_url(@project)}">#{@project.permalink}</a>)
14
+ end
15
+ end + [crumbs.last]
16
+ end
17
+
18
+ end
19
+ end
20
+ end