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,16 @@
1
+ module Integrity
2
+ module Helpers
3
+ module Gravatar
4
+ def gravatar_attrs(author, size=40)
5
+ # size = 40 unless %w(20 30 40 50).include?(size)
6
+ if author.email.nil?
7
+ {:src => "gravatar-#{size}.png", :title => 'Unknown author'}
8
+ else
9
+ email_digest = Digest::MD5.hexdigest(author.email)
10
+ source = "http://gravatar.com/avatar/#{email_digest}?s=#{size}"
11
+ {:src => source, :title => author.name}
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,45 @@
1
+ module Integrity
2
+ module Helpers
3
+ module PrettyOutput
4
+ def cycle(*values)
5
+ @cycles ||= {}
6
+ @cycles[values] ||= -1 # first value returned is 0
7
+ next_value = @cycles[values] = (@cycles[values] + 1) % values.size
8
+ values[next_value]
9
+ end
10
+
11
+ def bash_color_codes(string)
12
+ string.gsub("\e[0m", '</span>').
13
+ gsub("\e[31m", '<span class="color31">').
14
+ gsub("\e[32m", '<span class="color32">').
15
+ gsub("\e[33m", '<span class="color33">').
16
+ gsub("\e[34m", '<span class="color34">').
17
+ gsub("\e[35m", '<span class="color35">').
18
+ gsub("\e[36m", '<span class="color36">').
19
+ gsub("\e[37m", '<span class="color37">')
20
+ end
21
+
22
+ def pretty_date(date_time)
23
+ days_away = (Date.today - Date.new(date_time.year, date_time.month, date_time.day)).to_i
24
+ if days_away == 0
25
+ "today"
26
+ elsif days_away == 1
27
+ "yesterday"
28
+ else
29
+ strftime_with_ordinal(date_time, "on %b %d%o")
30
+ end
31
+ end
32
+
33
+ def strftime_with_ordinal(date_time, format_string)
34
+ ordinal = case date_time.day
35
+ when 1, 21, 31 then "st"
36
+ when 2, 22 then "nd"
37
+ when 3, 23 then "rd"
38
+ else "th"
39
+ end
40
+
41
+ date_time.strftime(format_string.gsub("%o", ordinal))
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ module Integrity
2
+ module Helpers
3
+ module Rendering
4
+ def stylesheets(*sheets)
5
+ sheets.each { |sheet|
6
+ haml_tag(:link, :href => root_path("/#{sheet}.css"),
7
+ :type => "text/css", :rel => "stylesheet")
8
+ }
9
+ end
10
+
11
+ def stylesheet_hash
12
+ @_hash ||= Digest::MD5.file(options.views + "/integrity.sass").hexdigest
13
+ end
14
+
15
+ def show(view, options={})
16
+ @title = breadcrumbs(*options[:title])
17
+ haml view
18
+ end
19
+
20
+ def partial(template, locals={})
21
+ haml("_#{template}".to_sym, :locals => locals, :layout => false)
22
+ end
23
+
24
+ def errors_on(object, field)
25
+ return "" unless errors = object.errors.on(field)
26
+ errors.map {|e| e.gsub(/#{field} /i, "") }.join(", ")
27
+ end
28
+
29
+ def error_class(object, field)
30
+ object.errors.on(field).nil? ? "" : "with_errors"
31
+ end
32
+
33
+ def checkbox(name, condition, extras={})
34
+ attrs = { :name => name, :type => "checkbox", :value => "1" }
35
+ attrs[:checked] = !!condition
36
+ attrs.update(extras)
37
+ end
38
+
39
+ def notifier_form
40
+ Notifier.available.each_pair { |name, klass|
41
+ haml_concat haml(klass.to_haml, :layout => :notifier, :locals => {
42
+ :notifier => name,
43
+ :enabled => current_project.notifies?(name),
44
+ :config => current_project.config_for(name) })
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ module Integrity
2
+ module Helpers
3
+ module Resources
4
+ def current_project
5
+ @project ||= Project.first(:permalink => params[:project]) or raise Sinatra::NotFound
6
+ end
7
+
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.update_notifiers(params["enabled_notifiers"] || [], params["notifiers"])
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,59 @@
1
+ module Integrity
2
+ module Helpers
3
+ module Urls
4
+ def root_url
5
+ @url ||= Addressable::URI.parse(base_url)
6
+ end
7
+
8
+ def root_path(path="")
9
+ url(path).path
10
+ end
11
+
12
+ def project_url(project, *path)
13
+ url("/" << [project.permalink, *path].flatten.join("/"))
14
+ end
15
+
16
+ def project_path(project, *path)
17
+ project_url(project, path).path
18
+ end
19
+
20
+ def commit_url(commit)
21
+ project_url(commit.project, "commits", commit.identifier)
22
+ end
23
+
24
+ def commit_path(commit, *path)
25
+ commit_url(commit).path
26
+ end
27
+
28
+ def build_path(build, *path)
29
+ warn "#build_path is deprecated, use #commit_path instead (#{caller[0]})"
30
+ commit_path build.commit, *path
31
+ end
32
+
33
+ def build_url(build)
34
+ warn "#build_url is deprecated, use #commit_url instead (#{caller[0]})"
35
+ commit_url build.commit
36
+ end
37
+
38
+ def push_url_for(project)
39
+ Addressable::URI.parse(project_url(project, "push")).tap do |url|
40
+ if Integrity.config[:use_basic_auth]
41
+ url.user = Integrity.config[:admin_username]
42
+ url.password = Integrity.config[:hash_admin_password] ?
43
+ "<password>" : Integrity.config[:admin_password]
44
+ end
45
+ end.to_s
46
+ end
47
+
48
+ private
49
+ def url(path="")
50
+ root_url.dup.tap { |url| url.path = root_url.path + path }
51
+ end
52
+
53
+ def base_url
54
+ Integrity.config[:base_uri] || ((respond_to?(:request) &&
55
+ request.respond_to?(:url)) ? request.url : fail("set base_uri"))
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,138 @@
1
+ require "rubygems"
2
+ require "thor"
3
+ require File.dirname(__FILE__) + "/../integrity"
4
+
5
+ module Integrity
6
+ class Installer < Thor
7
+ include FileUtils
8
+
9
+ desc "install [PATH]",
10
+ "Copy template files to PATH for desired deployement strategy
11
+ (either Thin, Passenger or Heroku). Next, go there and edit them."
12
+ method_options :passenger => :boolean,
13
+ :thin => :boolean,
14
+ :heroku => :boolean
15
+ def install(path)
16
+ @root = Pathname(path).expand_path
17
+
18
+ if options[:heroku]
19
+ cp_r Pathname(__FILE__).join("../../../config/heroku"), root
20
+ puts post_heroku_install_message
21
+ else
22
+ create_dir_structure
23
+ copy_template_files
24
+ edit_template_files
25
+ puts post_install_message
26
+ end
27
+ end
28
+
29
+ desc "migrate_db [CONFIG]",
30
+ "Checks the `database_uri` in CONFIG and migrates the
31
+ database up to the lastest version."
32
+ def migrate_db(config)
33
+ Integrity.new(config)
34
+
35
+ require "integrity/migrations"
36
+ Integrity.migrate_db
37
+ end
38
+
39
+ desc "launch [CONFIG]",
40
+ "Launch Integrity real quick."
41
+ method_options :config => :optional, :port => 4567
42
+ def launch
43
+ require "thin"
44
+ require "do_sqlite3"
45
+
46
+ File.file?(options[:config].to_s) ?
47
+ Integrity.new(options[:config]) : Integrity.new
48
+ Integrity.config[:base_uri] = "http://0.0.0.0:#{options[:port]}"
49
+
50
+ DataMapper.auto_migrate!
51
+
52
+ Thin::Server.start("0.0.0.0", options[:port], Integrity::App)
53
+ rescue LoadError => boom
54
+ missing_dependency = boom.message.split("--").last.lstrip
55
+ puts "Please install #{missing_dependency} to launch Integrity"
56
+ end
57
+
58
+ private
59
+ attr_reader :root
60
+
61
+ def create_dir_structure
62
+ mkdir_p root
63
+
64
+ mkdir_p root / "builds"
65
+ mkdir_p root / "log"
66
+
67
+ if options[:passenger]
68
+ mkdir_p root / "public"
69
+ mkdir_p root / "tmp"
70
+ end
71
+ end
72
+
73
+ def copy_template_files
74
+ copy "config.sample.ru"
75
+ copy "config.sample.yml"
76
+ copy "thin.sample.yml" if options[:thin]
77
+ end
78
+
79
+ def edit_template_files
80
+ edit_integrity_configuration
81
+ edit_thin_configuration if options[:thin]
82
+ end
83
+
84
+ def edit_integrity_configuration
85
+ config = File.read(root / "config.yml")
86
+ config.gsub! %r(sqlite3:///var/integrity.db), "sqlite3://#{root}/integrity.db"
87
+ config.gsub! %r(/path/to/scm/exports), "#{root}/builds"
88
+ config.gsub! %r(/var/log), "#{root}/log"
89
+ File.open(root / "config.yml", "w") { |f| f.puts config }
90
+ end
91
+
92
+ def edit_thin_configuration
93
+ config = File.read(root / "thin.yml")
94
+ config.gsub! %r(/apps/integrity), root
95
+ File.open(root / "thin.yml", 'w') { |f| f.puts config }
96
+ end
97
+
98
+ def post_heroku_install_message
99
+ <<EOF
100
+ Your Integrity install is ready to be deployed onto Heroku. Next steps:
101
+
102
+ 1. git init && git add . && git commit -am "Initial import"
103
+ 2. heroku create
104
+ 3. git push heroku master
105
+ 4. heroku rake db:migrate
106
+ EOF
107
+ end
108
+
109
+ def post_install_message
110
+ <<EOF
111
+ Awesome! Integrity was installed successfully!
112
+
113
+ To complete the installation, please configure the `database_uri` in
114
+ #{root.join("config.yml")} and install the matching DataMapper adapter if
115
+ necessary. Then, run `integrity migrate_db #{root.join("config.yml")}
116
+
117
+ == Notifiers
118
+ If you want to enable notifiers, install the gems and then require them
119
+ in #{root}/config.ru
120
+
121
+ For example:
122
+
123
+ sudo gem install -s http://gems.github.com foca-integrity-email
124
+
125
+ And then in #{root}/config.ru add:
126
+
127
+ require "notifier/email"
128
+
129
+ Don't forget to tweak #{root / "config.yml"} to your needs.
130
+ EOF
131
+ end
132
+
133
+ def copy(source)
134
+ cp(Pathname(__FILE__).dirname.join("../../config", source),
135
+ root.join(File.basename(source).gsub(/\.sample/, "")))
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,153 @@
1
+ require "dm-migrations"
2
+ require "migration_runner"
3
+
4
+ module Integrity
5
+ def self.migrate_db
6
+ setup_initial_migration if pre_migrations?
7
+ Integrity::Migrations.migrate_up!
8
+ end
9
+
10
+ def self.setup_initial_migration
11
+ database_adapter.execute %q(CREATE TABLE "migration_info" ("migration_name" VARCHAR(255));)
12
+ database_adapter.execute %q(INSERT INTO "migration_info" ("migration_name") VALUES ("initial"))
13
+ end
14
+
15
+ def self.pre_migrations?
16
+ !table_exists?("migration_info") &&
17
+ ( table_exists?("integrity_projects") &&
18
+ table_exists?("integrity_builds") &&
19
+ table_exists?("integrity_notifiers") )
20
+ end
21
+
22
+ def self.table_exists?(table_name)
23
+ database_adapter.storage_exists?(table_name)
24
+ end
25
+
26
+ def self.database_adapter
27
+ DataMapper.repository(:default).adapter
28
+ end
29
+
30
+ module Migrations
31
+ # This is what is actually happening:
32
+ # include DataMapper::MigrationRunner
33
+
34
+ include DataMapper::Types
35
+
36
+ migration 1, :initial, :verbose => true do
37
+ up do
38
+ create_table :integrity_projects do
39
+ column :id, Integer, :serial => true
40
+ column :name, String, :nullable => false
41
+ column :permalink, String
42
+ column :uri, URI, :nullable => false
43
+ column :branch, String, :nullable => false, :default => "master"
44
+ column :command, String, :nullable => false, :default => "rake"
45
+ column :public, Boolean, :default => true
46
+ column :building, Boolean, :default => false
47
+ column :created_at, DateTime
48
+ column :updated_at, DateTime
49
+
50
+ column :build_id, Integer
51
+ column :notifier_id, Integer
52
+ end
53
+
54
+ create_table :integrity_builds do
55
+ column :id, Integer, :serial => true
56
+ column :output, Text, :nullable => false, :default => ""
57
+ column :successful, Boolean, :nullable => false, :default => false
58
+ column :commit_identifier, String, :nullable => false
59
+ column :commit_metadata, Yaml, :nullable => false
60
+ column :created_at, DateTime
61
+ column :updated_at, DateTime
62
+
63
+ column :project_id, Integer
64
+ end
65
+
66
+ create_table :integrity_notifiers do
67
+ column :id, Integer, :serial => true
68
+ column :name, String, :nullable => false
69
+ column :config, Yaml, :nullable => false
70
+
71
+ column :project_id, Integer
72
+ end
73
+ end
74
+ end
75
+
76
+ migration 2, :add_commits, :verbose => true do
77
+ up do
78
+ class ::Integrity::Build
79
+ property :commit_identifier, String
80
+ property :commit_metadata, Yaml, :lazy => false
81
+ property :project_id, Integer
82
+ end
83
+
84
+ create_table :integrity_commits do
85
+ column :id, Integer, :serial => true
86
+ column :identifier, String, :nullable => false
87
+ column :message, String, :nullable => true, :length => 255
88
+ column :author, String, :nullable => true, :length => 255
89
+ column :url, String, :length => 255
90
+ column :committed_at, DateTime, :nullable => false
91
+ column :created_at, DateTime
92
+ column :updated_at, DateTime
93
+
94
+ column :project_id, Integer
95
+ end
96
+
97
+ modify_table :integrity_builds do
98
+ add_column :commit_id, Integer
99
+ add_column :started_at, DateTime
100
+ add_column :completed_at, DateTime
101
+ end
102
+
103
+ # Die, orphans, die
104
+ Build.all(:project_id => nil).destroy!
105
+
106
+ # sqlite hodgepockery
107
+ all_builds = Build.all.each {|b| b.freeze }
108
+ drop_table :integrity_builds
109
+ create_table :integrity_builds do
110
+ column :id, Integer, :serial => true
111
+ column :started_at, DateTime
112
+ column :completed_at, DateTime
113
+ column :successful, Boolean
114
+ column :output, Text, :nullable => false, :default => ""
115
+ column :created_at, DateTime
116
+ column :updated_at, DateTime
117
+
118
+ column :commit_id, Integer
119
+ end
120
+
121
+ all_builds.each do |build|
122
+ commit = Commit.first(:identifier => build.commit_identifier)
123
+
124
+ if commit.nil?
125
+ commit = Commit.create(:identifier => build.commit_identifier,
126
+ :message => build.commit_metadata[:message],
127
+ :author => build.commit_metadata[:author],
128
+ :url => build.commit_metadata[:url],
129
+ :committed_at => build.commit_metadata[:date],
130
+ :project_id => build.project_id)
131
+ end
132
+
133
+ Build.create(:commit_id => commit.id,
134
+ :started_at => build.created_at,
135
+ :completed_at => build.updated_at,
136
+ :successful => build.successful,
137
+ :output => build.output)
138
+ end
139
+ end
140
+ end
141
+
142
+ migration 3, :add_enabled_column do
143
+ up do
144
+ modify_table(:integrity_notifiers) { add_column :enabled, Boolean }
145
+ end
146
+
147
+ down do
148
+ # TODO: sqlite doesn't support DROP COLUMN ...
149
+ # modify_table(:integrity_notifiers) { drop_column :enabled }
150
+ end
151
+ end
152
+ end
153
+ end