brycethornton-integrity 0.1.7.1

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 (57) hide show
  1. data/README.markdown +66 -0
  2. data/Rakefile +110 -0
  3. data/VERSION.yml +4 -0
  4. data/app.rb +137 -0
  5. data/bin/integrity +4 -0
  6. data/config/config.sample.ru +31 -0
  7. data/config/config.sample.yml +38 -0
  8. data/config/thin.sample.yml +13 -0
  9. data/integrity.gemspec +76 -0
  10. data/lib/integrity.rb +82 -0
  11. data/lib/integrity/build.rb +61 -0
  12. data/lib/integrity/core_ext/object.rb +6 -0
  13. data/lib/integrity/core_ext/string.rb +5 -0
  14. data/lib/integrity/helpers.rb +16 -0
  15. data/lib/integrity/helpers/authorization.rb +33 -0
  16. data/lib/integrity/helpers/breadcrumbs.rb +20 -0
  17. data/lib/integrity/helpers/forms.rb +28 -0
  18. data/lib/integrity/helpers/pretty_output.rb +45 -0
  19. data/lib/integrity/helpers/rendering.rb +14 -0
  20. data/lib/integrity/helpers/resources.rb +13 -0
  21. data/lib/integrity/helpers/urls.rb +47 -0
  22. data/lib/integrity/installer.rb +132 -0
  23. data/lib/integrity/migrations.rb +152 -0
  24. data/lib/integrity/notifier.rb +50 -0
  25. data/lib/integrity/notifier/base.rb +55 -0
  26. data/lib/integrity/project.rb +117 -0
  27. data/lib/integrity/project_builder.rb +47 -0
  28. data/lib/integrity/scm.rb +19 -0
  29. data/lib/integrity/scm/git.rb +91 -0
  30. data/lib/integrity/scm/git/uri.rb +57 -0
  31. data/public/buttons.css +82 -0
  32. data/public/reset.css +7 -0
  33. data/public/spinner.gif +0 -0
  34. data/test/helpers.rb +48 -0
  35. data/test/helpers/acceptance.rb +126 -0
  36. data/test/helpers/acceptance/git_helper.rb +99 -0
  37. data/test/helpers/acceptance/textfile_notifier.rb +26 -0
  38. data/test/helpers/expectations.rb +5 -0
  39. data/test/helpers/expectations/be_a.rb +23 -0
  40. data/test/helpers/expectations/change.rb +90 -0
  41. data/test/helpers/expectations/have.rb +105 -0
  42. data/test/helpers/expectations/have_tag.rb +128 -0
  43. data/test/helpers/expectations/predicates.rb +37 -0
  44. data/test/helpers/fixtures.rb +83 -0
  45. data/views/_build_info.haml +18 -0
  46. data/views/build.haml +2 -0
  47. data/views/error.haml +36 -0
  48. data/views/home.haml +23 -0
  49. data/views/integrity.sass +387 -0
  50. data/views/layout.haml +28 -0
  51. data/views/new.haml +51 -0
  52. data/views/not_found.haml +31 -0
  53. data/views/notifier.haml +7 -0
  54. data/views/project.builder +21 -0
  55. data/views/project.haml +28 -0
  56. data/views/unauthorized.haml +38 -0
  57. metadata +243 -0
@@ -0,0 +1,91 @@
1
+ module Integrity
2
+ module SCM
3
+ class Git
4
+ require File.dirname(__FILE__) / "git/uri"
5
+
6
+ attr_reader :uri, :branch, :working_directory
7
+
8
+ def self.working_tree_path(uri)
9
+ Git::URI.new(uri).working_tree_path
10
+ end
11
+
12
+ def initialize(uri, branch, working_directory)
13
+ @uri = uri.to_s
14
+ @branch = branch.to_s
15
+ @working_directory = working_directory
16
+ end
17
+
18
+ def with_revision(revision)
19
+ fetch_code
20
+ checkout(revision)
21
+ yield
22
+ end
23
+
24
+ def commit_identifier(sha1)
25
+ `cd #{working_directory} && git show -s --pretty=format:%H #{sha1}`.chomp
26
+ end
27
+
28
+ def commit_metadata(sha1)
29
+ format = %Q(---%n:author: %an <%ae>%n:message: >-%n %s%n:date: %ci%n)
30
+ YAML.load(`cd #{working_directory} && git show -s --pretty=format:"#{format}" #{sha1}`)
31
+ end
32
+
33
+ def name
34
+ self.class.name.split("::").last
35
+ end
36
+
37
+ private
38
+
39
+ def fetch_code
40
+ clone unless cloned?
41
+ checkout unless on_branch?
42
+ pull
43
+ submodule_update
44
+ end
45
+
46
+ def clone
47
+ log "Cloning #{uri} to #{working_directory}"
48
+ `git clone #{uri} #{working_directory} &>/dev/null`
49
+ end
50
+
51
+ def checkout(treeish=nil)
52
+ strategy = case
53
+ when treeish then treeish
54
+ when local_branches.include?(branch) then branch
55
+ else "-b #{branch} origin/#{branch}"
56
+ end
57
+
58
+ log "Checking-out #{strategy}"
59
+ `cd #{working_directory} && git checkout #{strategy} &>/dev/null`
60
+ end
61
+
62
+ def pull
63
+ log "Pull-ing in #{working_directory}"
64
+ `cd #{working_directory} && git pull &>/dev/null`
65
+ end
66
+
67
+ def submodule_update
68
+ if File.exists?(File.join(working_directory, '.gitmodules'))
69
+ log "Updating submodules in #{working_directory}"
70
+ `cd #{working_directory} && git submodule init && git submodule update &>/dev/null`
71
+ end
72
+ end
73
+
74
+ def local_branches
75
+ `cd #{working_directory} && git branch`.split("\n").map {|b| b.delete("*").strip }
76
+ end
77
+
78
+ def cloned?
79
+ File.directory?(working_directory / ".git")
80
+ end
81
+
82
+ def on_branch?
83
+ File.basename(`cd #{working_directory} && git symbolic-ref HEAD &>/dev/null`).chomp == branch
84
+ end
85
+
86
+ def log(message)
87
+ Integrity.log("Git") { message }
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,57 @@
1
+ module Integrity
2
+ module SCM
3
+ class Git
4
+ # From the git-pull man page:
5
+ #
6
+ # GIT URLS
7
+ # One of the following notations can be used to name the remote repository:
8
+ #
9
+ # rsync://host.xz/path/to/repo.git/
10
+ # http://host.xz/path/to/repo.git/
11
+ # git://host.xz/~user/path/to/repo.git/
12
+ # ssh://[user@]host.xz[:port]/path/to/repo.git/
13
+ # ssh://[user@]host.xz/path/to/repo.git/
14
+ # ssh://[user@]host.xz/~user/path/to/repo.git/
15
+ # ssh://[user@]host.xz/~/path/to/repo.git
16
+ #
17
+ # SSH is the default transport protocol over the network. You can optionally
18
+ # specify which user to log-in as, and an alternate, scp-like syntax is also
19
+ # supported
20
+ #
21
+ # Both syntaxes support username expansion, as does the native git protocol,
22
+ # but only the former supports port specification. The following three are
23
+ # identical to the last three above, respectively:
24
+ #
25
+ # [user@]host.xz:/path/to/repo.git/
26
+ # [user@]host.xz:~user/path/to/repo.git/
27
+ # [user@]host.xz:path/to/repo.git
28
+ #
29
+ class URI
30
+ def initialize(uri_string)
31
+ @uri = Addressable::URI.parse(uri_string)
32
+ end
33
+
34
+ def working_tree_path
35
+ strip_extension(path).gsub("/", "-")
36
+ end
37
+
38
+ private
39
+
40
+ def strip_extension(string)
41
+ uri = Pathname.new(string)
42
+ if uri.extname.any?
43
+ uri = Pathname.new(string)
44
+ string.gsub(Regexp.new("#{uri.extname}\/?"), "")
45
+ else
46
+ string
47
+ end
48
+ end
49
+
50
+ def path
51
+ path = @uri.path
52
+ path.gsub(/\~[a-zA-Z0-9]*\//, "").gsub(/^\//, "")
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,82 @@
1
+ /* --------------------------------------------------------------
2
+
3
+ buttons.css
4
+ * Gives you some great CSS-only buttons.
5
+
6
+ Created by Kevin Hale [particletree.com]
7
+ * particletree.com/features/rediscovering-the-button-element
8
+
9
+ See Readme.txt in this folder for instructions.
10
+
11
+ -------------------------------------------------------------- */
12
+
13
+ button {
14
+ display:block;
15
+ float:left;
16
+ margin:0 0.583em 0.667em 0;
17
+ padding:5px 10px 5px 7px; /* Links */
18
+
19
+ border:1px solid #dedede;
20
+ border-top:1px solid #eee;
21
+ border-left:1px solid #eee;
22
+
23
+ background-color:#f5f5f5;
24
+ font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
25
+ font-size:100%;
26
+ line-height:130%;
27
+ text-decoration:none;
28
+ font-weight:bold;
29
+ color:#565656;
30
+ cursor:pointer;
31
+ }
32
+ button {
33
+ width:auto;
34
+ overflow:visible;
35
+ padding:4px 10px 3px 7px; /* IE6 */
36
+ }
37
+ button[type] {
38
+ padding:4px 10px 4px 7px; /* Firefox */
39
+ line-height:17px; /* Safari */
40
+ }
41
+ *:first-child+html button[type] {
42
+ padding:4px 10px 3px 7px; /* IE7 */
43
+ }
44
+ button img {
45
+ margin:0 3px -3px 0 !important;
46
+ padding:0;
47
+ border:none;
48
+ width:16px;
49
+ height:16px;
50
+ float:none;
51
+ }
52
+
53
+
54
+ /* Button colors
55
+ -------------------------------------------------------------- */
56
+
57
+ /* Standard */
58
+ button:hover {
59
+ background-color:#dff4ff;
60
+ border:1px solid #c2e1ef;
61
+ color:#336699;
62
+ }
63
+
64
+ /* Positive */
65
+ body .positive {
66
+ color:#529214;
67
+ }
68
+ button.positive:hover {
69
+ background-color:#E6EFC2;
70
+ border:1px solid #C6D880;
71
+ color:#529214;
72
+ }
73
+
74
+ /* Negative */
75
+ body .negative {
76
+ color:#d12f19;
77
+ }
78
+ button.negative:hover {
79
+ background:#fbe3e4;
80
+ border:1px solid #fbc2c4;
81
+ color:#d12f19;
82
+ }
data/public/reset.css ADDED
@@ -0,0 +1,7 @@
1
+ /*
2
+ Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3
+ Code licensed under the BSD License:
4
+ http://developer.yahoo.net/yui/license.txt
5
+ version: 2.5.2
6
+ */
7
+ html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;font-variant:normal;}sup {vertical-align:text-top;}sub {vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
Binary file
data/test/helpers.rb ADDED
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + "/../lib/integrity"
2
+
3
+ begin
4
+ require "test/unit"
5
+ require "redgreen"
6
+ require "context"
7
+ require "storyteller"
8
+ require "pending"
9
+ require "matchy"
10
+ require "rr"
11
+ require "mocha"
12
+ require "ruby-debug"
13
+ rescue LoadError
14
+ puts "You're missing some gems required to run the tests."
15
+ puts "Please run `rake test:install_dependencies`"
16
+ puts "You'll probably need to run that command as root or with sudo."
17
+ puts
18
+ puts "Thanks :)"
19
+ puts
20
+
21
+ exit 1
22
+ end
23
+
24
+ require File.dirname(__FILE__) / "helpers" / "expectations"
25
+ require File.dirname(__FILE__) / "helpers" / "fixtures"
26
+ require File.dirname(__FILE__) / "helpers" / "acceptance"
27
+
28
+ module TestHelper
29
+ def setup_and_reset_database!
30
+ DataMapper.setup(:default, "sqlite3::memory:")
31
+ DataMapper.auto_migrate!
32
+ end
33
+
34
+ def ignore_logs!
35
+ stub(Integrity).log { nil }
36
+ end
37
+ end
38
+
39
+ class Test::Unit::TestCase
40
+ class << self
41
+ alias_method :specify, :test
42
+ end
43
+
44
+ include RR::Adapters::TestUnit
45
+ include Integrity
46
+ include TestHelper
47
+ end
48
+
@@ -0,0 +1,126 @@
1
+ require 'webrat/rack'
2
+ require 'sinatra'
3
+ require 'sinatra/test'
4
+
5
+ disable :run
6
+ disable :reload
7
+
8
+ Webrat.configuration.instance_variable_set("@mode", :sinatra)
9
+
10
+ module Webrat
11
+ class SinatraSession < Session
12
+ DEFAULT_DOMAIN = "integrity.example.org"
13
+
14
+ def initialize(context = nil)
15
+ super(context)
16
+ @sinatra_test = Sinatra::TestHarness.new
17
+ end
18
+
19
+ %w(get head post put delete).each do |verb|
20
+ class_eval <<-METHOD
21
+ def #{verb}(path, data, headers = {})
22
+ params = data.inject({}) do |data, (key,value)|
23
+ data[key] = Rack::Utils.unescape(value)
24
+ data
25
+ end
26
+ headers['HTTP_HOST'] = DEFAULT_DOMAIN
27
+ @sinatra_test.#{verb}(path, params, headers)
28
+ end
29
+ METHOD
30
+ end
31
+
32
+ def response_body
33
+ @sinatra_test.body
34
+ end
35
+
36
+ def response_code
37
+ @sinatra_test.status
38
+ end
39
+
40
+ private
41
+
42
+ def response
43
+ @sinatra_test.response
44
+ end
45
+
46
+ def current_host
47
+ URI.parse(current_url).host || DEFAULT_DOMAIN
48
+ end
49
+
50
+ def response_location_host
51
+ URI.parse(response_location).host || DEFAULT_DOMAIN
52
+ end
53
+ end
54
+ end
55
+
56
+ require Integrity.root / "app"
57
+ require File.dirname(__FILE__) / "acceptance/git_helper"
58
+
59
+ module AcceptanceHelper
60
+ include FileUtils
61
+
62
+ def export_directory
63
+ Integrity.root / "exports"
64
+ end
65
+
66
+ def enable_auth!
67
+ Integrity.config[:use_basic_auth] = true
68
+ Integrity.config[:admin_username] = "admin"
69
+ Integrity.config[:admin_password] = "test"
70
+ Integrity.config[:hash_admin_password] = false
71
+ end
72
+
73
+ def login_as(user, password)
74
+ def AcceptanceHelper.logged_in; true; end
75
+ basic_auth user, password
76
+ visit "/login"
77
+ Sinatra::Application.before { login_required if AcceptanceHelper.logged_in }
78
+ end
79
+
80
+ def log_out
81
+ def AcceptanceHelper.logged_in; false; end
82
+ @_webrat_session = Webrat::SinatraSession.new(self)
83
+ end
84
+
85
+ def disable_auth!
86
+ Integrity.config[:use_basic_auth] = false
87
+ end
88
+
89
+ def set_and_create_export_directory!
90
+ FileUtils.rm_r(export_directory) if File.directory?(export_directory)
91
+ FileUtils.mkdir(export_directory)
92
+ Integrity.config[:export_directory] = export_directory
93
+ end
94
+
95
+ def setup_log!
96
+ pathname = Integrity.root / "integrity.log"
97
+ FileUtils.rm pathname if File.exists?(pathname)
98
+ Integrity.config[:log] = pathname
99
+ end
100
+ end
101
+
102
+ class Test::Unit::AcceptanceTestCase < Test::Unit::TestCase
103
+ include AcceptanceHelper
104
+ include Test::Storyteller
105
+ include GitHelper
106
+ include Webrat::Methods
107
+ Webrat::Methods.delegate_to_session :response_code
108
+
109
+ before(:all) do
110
+ Integrity.config[:base_uri] = "http://#{Webrat::SinatraSession::DEFAULT_DOMAIN}"
111
+ end
112
+
113
+ before(:each) do
114
+ # ensure each scenario is run in a clean sandbox
115
+ setup_and_reset_database!
116
+ enable_auth!
117
+ setup_log!
118
+ set_and_create_export_directory!
119
+ log_out
120
+ end
121
+
122
+ after(:each) do
123
+ destroy_all_git_repos
124
+ rm_r export_directory if File.directory?(export_directory)
125
+ end
126
+ end
@@ -0,0 +1,99 @@
1
+ module GitHelper
2
+ @@_git_repositories = Hash.new {|h,k| h[k] = Repo.new(k) }
3
+
4
+ def git_repo(name)
5
+ @@_git_repositories[name]
6
+ end
7
+
8
+ def destroy_all_git_repos
9
+ @@_git_repositories.each {|n,r| r.destroy }
10
+ @@_git_repositories.clear
11
+ end
12
+
13
+ class Repo
14
+ attr_reader :path
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ @path = "/tmp" / @name.to_s
19
+ create
20
+ end
21
+
22
+ def path
23
+ @path / ".git"
24
+ end
25
+
26
+ def create
27
+ destroy
28
+ FileUtils.mkdir_p @path
29
+
30
+ Dir.chdir(@path) do
31
+ system 'git init &>/dev/null'
32
+ system 'git config user.name "John Doe"'
33
+ system 'git config user.email "johndoe@example.org"'
34
+ system 'echo "just a test repo" >> README'
35
+ system 'git add README &>/dev/null'
36
+ system 'git commit -m "First commit" &>/dev/null'
37
+ end
38
+
39
+ add_successful_commit
40
+ end
41
+
42
+ def commits
43
+ Dir.chdir(@path) do
44
+ commits = `git log --pretty=oneline`.collect { |line| line.split(" ").first }
45
+ commits.inject([]) do |commits, sha1|
46
+ format = %Q(---%n:message: >-%n %s%n:timestamp: %ci%n:id: #{sha1})
47
+ commits << YAML.load(`git show -s --pretty=format:"#{format}" #{sha1}`)
48
+ end
49
+ end
50
+ end
51
+
52
+ def add_commit(message, &action)
53
+ Dir.chdir(@path) do
54
+ yield action
55
+ system %Q(git commit -m "#{message}" &>/dev/null)
56
+ end
57
+ end
58
+
59
+ def add_failing_commit
60
+ add_commit "This commit will fail" do
61
+ system %Q(echo '#{build_script(false)}' > test)
62
+ system %Q(chmod +x test)
63
+ system %Q(git add test &>/dev/null)
64
+ end
65
+ end
66
+
67
+ def add_successful_commit
68
+ add_commit "This commit will work" do
69
+ system %Q(echo '#{build_script(true)}' > test)
70
+ system %Q(chmod +x test)
71
+ system %Q(git add test &>/dev/null)
72
+ end
73
+ end
74
+
75
+ def head
76
+ Dir.chdir(@path) do
77
+ `git log --pretty=format:%H | head -1`.chomp
78
+ end
79
+ end
80
+
81
+ def short_head
82
+ head[0..6]
83
+ end
84
+
85
+ def destroy
86
+ FileUtils.rm_rf @path if File.directory?(@path)
87
+ end
88
+
89
+ protected
90
+
91
+ def build_script(successful=true)
92
+ <<-script
93
+ #!/bin/sh
94
+ echo "Running tests..."
95
+ exit #{successful ? 0 : 1}
96
+ script
97
+ end
98
+ end
99
+ end