pivotal-piston 1.9.4

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 (96) hide show
  1. data/.gitignore +8 -0
  2. data/History.txt +11 -0
  3. data/License.txt +20 -0
  4. data/Manifest.txt +85 -0
  5. data/README.txt +136 -0
  6. data/Rakefile +4 -0
  7. data/bin/piston +5 -0
  8. data/config/hoe.rb +79 -0
  9. data/config/requirements.rb +18 -0
  10. data/lib/piston.rb +18 -0
  11. data/lib/piston/cli.rb +315 -0
  12. data/lib/piston/commands.rb +4 -0
  13. data/lib/piston/commands/base.rb +44 -0
  14. data/lib/piston/commands/import.rb +42 -0
  15. data/lib/piston/commands/info.rb +14 -0
  16. data/lib/piston/commands/lock_unlock.rb +21 -0
  17. data/lib/piston/commands/update.rb +29 -0
  18. data/lib/piston/git.rb +12 -0
  19. data/lib/piston/git/client.rb +77 -0
  20. data/lib/piston/git/commit.rb +74 -0
  21. data/lib/piston/git/repository.rb +63 -0
  22. data/lib/piston/git/working_copy.rb +86 -0
  23. data/lib/piston/repository.rb +57 -0
  24. data/lib/piston/revision.rb +53 -0
  25. data/lib/piston/svn.rb +14 -0
  26. data/lib/piston/svn/client.rb +88 -0
  27. data/lib/piston/svn/repository.rb +67 -0
  28. data/lib/piston/svn/revision.rb +74 -0
  29. data/lib/piston/svn/working_copy.rb +108 -0
  30. data/lib/piston/version.rb +9 -0
  31. data/lib/piston/working_copy.rb +183 -0
  32. data/lib/subclass_responsibility_error.rb +2 -0
  33. data/log/.gitignore +0 -0
  34. data/samples/common.rb +19 -0
  35. data/samples/import_git_git.rb +39 -0
  36. data/samples/import_git_svn.rb +36 -0
  37. data/samples/import_svn_git.rb +29 -0
  38. data/samples/import_svn_svn.rb +24 -0
  39. data/script/destroy +14 -0
  40. data/script/generate +14 -0
  41. data/script/txt2html +74 -0
  42. data/setup.rb +1585 -0
  43. data/tasks/deployment.rake +34 -0
  44. data/tasks/environment.rake +7 -0
  45. data/tasks/samples.rake +6 -0
  46. data/tasks/test.rake +69 -0
  47. data/tasks/website.rake +17 -0
  48. data/test/integration/test_git_git.rb +99 -0
  49. data/test/integration/test_git_svn.rb +121 -0
  50. data/test/integration/test_import_svn_git.rb +47 -0
  51. data/test/integration/test_import_svn_svn.rb +38 -0
  52. data/test/integration_helpers.rb +33 -0
  53. data/test/test_helper.rb +83 -0
  54. data/test/unit/git/commit/test_checkout.rb +31 -0
  55. data/test/unit/git/commit/test_each.rb +30 -0
  56. data/test/unit/git/commit/test_rememberance.rb +21 -0
  57. data/test/unit/git/commit/test_validation.rb +34 -0
  58. data/test/unit/git/repository/test_at.rb +23 -0
  59. data/test/unit/git/repository/test_basename.rb +12 -0
  60. data/test/unit/git/repository/test_branchanme.rb +15 -0
  61. data/test/unit/git/repository/test_guessing.rb +32 -0
  62. data/test/unit/git/working_copy/test_copying.rb +25 -0
  63. data/test/unit/git/working_copy/test_creation.rb +22 -0
  64. data/test/unit/git/working_copy/test_existence.rb +18 -0
  65. data/test/unit/git/working_copy/test_finalization.rb +15 -0
  66. data/test/unit/git/working_copy/test_guessing.rb +35 -0
  67. data/test/unit/git/working_copy/test_rememberance.rb +21 -0
  68. data/test/unit/svn/repository/test_at.rb +19 -0
  69. data/test/unit/svn/repository/test_basename.rb +24 -0
  70. data/test/unit/svn/repository/test_guessing.rb +45 -0
  71. data/test/unit/svn/revision/test_checkout.rb +28 -0
  72. data/test/unit/svn/revision/test_each.rb +22 -0
  73. data/test/unit/svn/revision/test_rememberance.rb +38 -0
  74. data/test/unit/svn/revision/test_validation.rb +50 -0
  75. data/test/unit/svn/working_copy/test_copying.rb +26 -0
  76. data/test/unit/svn/working_copy/test_creation.rb +16 -0
  77. data/test/unit/svn/working_copy/test_existence.rb +23 -0
  78. data/test/unit/svn/working_copy/test_externals.rb +56 -0
  79. data/test/unit/svn/working_copy/test_finalization.rb +17 -0
  80. data/test/unit/svn/working_copy/test_guessing.rb +18 -0
  81. data/test/unit/svn/working_copy/test_merging.rb +47 -0
  82. data/test/unit/svn/working_copy/test_rememberance.rb +26 -0
  83. data/test/unit/test_info.rb +37 -0
  84. data/test/unit/test_lock_unlock.rb +47 -0
  85. data/test/unit/test_repository.rb +51 -0
  86. data/test/unit/test_revision.rb +31 -0
  87. data/test/unit/working_copy/test_guessing.rb +35 -0
  88. data/test/unit/working_copy/test_info.rb +14 -0
  89. data/test/unit/working_copy/test_rememberance.rb +42 -0
  90. data/test/unit/working_copy/test_validate.rb +63 -0
  91. data/website/index.html +11 -0
  92. data/website/index.txt +39 -0
  93. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  94. data/website/stylesheets/screen.css +138 -0
  95. data/website/template.rhtml +48 -0
  96. metadata +244 -0
@@ -0,0 +1,57 @@
1
+ require "piston/revision"
2
+
3
+ module Piston
4
+ class Repository
5
+ class UnhandledUrl < RuntimeError; end
6
+
7
+ class << self
8
+ def logger
9
+ @@logger ||= Log4r::Logger["handler"]
10
+ end
11
+
12
+ def guess(url)
13
+ logger.info {"Guessing the repository type of #{url.inspect}"}
14
+
15
+ handler = handlers.detect do |handler|
16
+ logger.debug {"Asking #{handler}"}
17
+ handler.understands_url?(url)
18
+ end
19
+
20
+ raise UnhandledUrl unless handler
21
+
22
+ handler.new(url)
23
+ end
24
+
25
+ @@handlers = Array.new
26
+ def add_handler(handler)
27
+ @@handlers << handler
28
+ end
29
+
30
+ def handlers
31
+ @@handlers
32
+ end
33
+ end
34
+
35
+ attr_reader :url
36
+
37
+ def initialize(url)
38
+ @url = url
39
+ end
40
+
41
+ def logger
42
+ self.class.logger
43
+ end
44
+
45
+ def at(revision)
46
+ raise SubclassResponsibilityError, "Piston::Repository#at should be implemented by a subclass."
47
+ end
48
+
49
+ def to_s
50
+ "Piston::Repository(#{@url})"
51
+ end
52
+
53
+ def ==(other)
54
+ url == other.url
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,53 @@
1
+ module Piston
2
+ class Revision
3
+ include Enumerable
4
+
5
+ class << self
6
+ def logger
7
+ @@logger ||= Log4r::Logger["handler"]
8
+ end
9
+ end
10
+
11
+ attr_reader :repository, :revision, :recalled_values, :dir
12
+
13
+ def initialize(repository, revision, recalled_values={})
14
+ @repository, @revision, @recalled_values = repository, revision, recalled_values
15
+ end
16
+
17
+ def url
18
+ repository.url
19
+ end
20
+
21
+ def name
22
+ @revision
23
+ end
24
+
25
+ def logger
26
+ self.class.logger
27
+ end
28
+
29
+ def to_s
30
+ "Piston::Revision(#{@repository.url}@#{@revision})"
31
+ end
32
+
33
+ # Retrieve a copy of this repository into +dir+.
34
+ def checkout_to(dir)
35
+ logger.debug {"Checking out #{@repository}@#{@revision} into #{dir}"}
36
+ @dir = dir.kind_of?(Pathname) ? dir : Pathname.new(dir)
37
+ end
38
+
39
+ # What values does this revision want to remember for the future ?
40
+ def remember_values
41
+ logger.debug {"Generating remember values"}
42
+ {}
43
+ end
44
+
45
+ # Yields each file of this revision in turn to our caller.
46
+ def each
47
+ end
48
+
49
+ # Copies +relpath+ (relative to ourselves) to +abspath+ (an absolute path).
50
+ def copy_to(relpath, abspath)
51
+ end
52
+ end
53
+ end
data/lib/piston/svn.rb ADDED
@@ -0,0 +1,14 @@
1
+ require "piston/svn/client"
2
+ require "piston/svn/repository"
3
+ require "piston/svn/revision"
4
+ require "piston/svn/working_copy"
5
+
6
+ module Piston
7
+ module Svn
8
+ ROOT = "piston:root"
9
+ UUID = "piston:uuid"
10
+ REMOTE_REV = "piston:remote-revision"
11
+ LOCAL_REV = "piston:local-revision"
12
+ LOCKED = "piston:locked"
13
+ end
14
+ end
@@ -0,0 +1,88 @@
1
+ require "singleton"
2
+
3
+ module Piston
4
+ module Svn
5
+ class Client
6
+ include Singleton
7
+
8
+ class CommandError < RuntimeError; end
9
+ class Failed < CommandError; end
10
+ class BadCommand < CommandError; end
11
+
12
+ def logger
13
+ @logger ||= Log4r::Logger["handler::client"]
14
+ end
15
+
16
+ def out_logger
17
+ @out_logger ||= Log4r::Logger["handler::client::out"]
18
+ end
19
+
20
+ def svnadmin(*args)
21
+ run_cmd :svnadmin, *args
22
+ end
23
+
24
+ def svn(*args)
25
+ run_cmd :svn, *args
26
+ end
27
+
28
+ def svnlook(*args)
29
+ run_cmd :svnlook, *args
30
+ end
31
+
32
+ def svnversion(*args)
33
+ run_cmd :svnversion, *args
34
+ end
35
+
36
+ private
37
+ def run_cmd(executable, *args)
38
+ args.collect! {|arg| arg =~ /\s|\*|\?|"|\n|\r/ ? %Q('#{arg}') : arg}
39
+ args.collect! {|arg| arg ? arg : '""'}
40
+ cmd = %Q|#{executable} #{args.join(' ')}|
41
+ logger.debug {"> " + cmd}
42
+
43
+ original_language = ENV["LANGUAGE"]
44
+ begin
45
+ ENV["LANGUAGE"] = "C"
46
+ value = run_real(cmd)
47
+ out_logger.info {"< " + value} unless (value || "").strip.empty?
48
+ return value
49
+ ensure
50
+ ENV["LANGUAGE"] = original_language
51
+ end
52
+ end
53
+
54
+ begin
55
+ raise LoadError, "Not implemented on Win32 machines" if RUBY_PLATFORM =~ /mswin32/
56
+
57
+ begin
58
+ require "rubygems"
59
+ rescue LoadError
60
+ # NOP -- attempt to load without Rubygems
61
+ end
62
+
63
+ require "open4"
64
+
65
+ def run_real(cmd)
66
+ begin
67
+ pid, stdin, stdout, stderr = Open4::popen4(cmd)
68
+ _, cmdstatus = Process.waitpid2(pid)
69
+ raise CommandError, "#{cmd.inspect} exited with status: #{cmdstatus.exitstatus}\n#{stderr.read}" unless cmdstatus.success?
70
+ return stdout.read
71
+ rescue Errno::ENOENT
72
+ raise BadCommand, cmd.inspect
73
+ end
74
+ end
75
+
76
+ rescue LoadError
77
+ # On platforms where open4 is unavailable, we fallback to running using
78
+ # the backtick method of Kernel.
79
+ def run_real(cmd)
80
+ out = `#{cmd}`
81
+ raise BadCommand, cmd.inspect if $?.exitstatus == 127
82
+ raise Failed, "#{cmd.inspect} exited with status: #{$?.exitstatus}" unless $?.success?
83
+ out
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,67 @@
1
+ require "uri"
2
+
3
+ module Piston
4
+ module Svn
5
+ class Repository < Piston::Repository
6
+ # Register ourselves as a repository handler
7
+ Piston::Repository.add_handler self
8
+
9
+ class << self
10
+ def understands_url?(url)
11
+ uri = URI.parse(url)
12
+ case uri.scheme
13
+ when "svn", /^svn\+/
14
+ true
15
+ when "http", "https", "file"
16
+ # Have to contact server to know
17
+ result = svn(:info, url) rescue :failed
18
+ result == :failed ? false : true
19
+ else
20
+ # Don't know how to handle this scheme.
21
+ # Let someone else handle it
22
+ end
23
+ end
24
+
25
+ def client
26
+ @@client ||= Piston::Svn::Client.instance
27
+ end
28
+
29
+ def svn(*args)
30
+ client.svn(*args)
31
+ end
32
+
33
+ def repository_type
34
+ 'svn'
35
+ end
36
+ end
37
+
38
+ def svn(*args)
39
+ self.class.svn(*args)
40
+ end
41
+
42
+ def at(revision)
43
+ if revision.respond_to?(:keys) then
44
+ rev = revision[Piston::Svn::REMOTE_REV]
45
+ Piston::Svn::Revision.new(self, rev, revision)
46
+ else
47
+ case
48
+ when revision == :head
49
+ Piston::Svn::Revision.new(self, "HEAD")
50
+ when revision.to_i != 0
51
+ Piston::Svn::Revision.new(self, revision.to_i)
52
+ else
53
+ raise ArgumentError, "Invalid revision argument: #{revision.inspect}"
54
+ end
55
+ end
56
+ end
57
+
58
+ def basename
59
+ if self.url =~ /trunk|branches|tags/ then
60
+ self.url.sub(%r{/(?:trunk|branches|tags).*$}, "").split("/").last
61
+ else
62
+ self.url.split("/").last
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,74 @@
1
+ require "piston/revision"
2
+ require "fileutils"
3
+
4
+ module Piston
5
+ module Svn
6
+ class Revision < Piston::Revision
7
+ class InvalidRevision < RuntimeError; end
8
+ class RepositoryMoved < InvalidRevision; end
9
+ class UuidChanged < InvalidRevision; end
10
+
11
+ def client
12
+ @client ||= Piston::Svn::Client.instance
13
+ end
14
+
15
+ def svn(*args)
16
+ client.svn(*args)
17
+ end
18
+
19
+ def name
20
+ "r#{revision}"
21
+ end
22
+
23
+ def checkout_to(dir)
24
+ super
25
+ answer = svn(:checkout, "--revision", revision, repository.url, dir)
26
+ if answer =~ /Checked out revision (\d+)[.]/ then
27
+ if revision == "HEAD" then
28
+ @revision = $1.to_i
29
+ elsif revision != $1.to_i then
30
+ raise Failed, "Did not get the revision I wanted to checkout. Subversion checked out #{$1}, I wanted #{revision}"
31
+ end
32
+ else
33
+ raise Failed, "Could not checkout revision #{revision} from #{repository.url} to #{dir}\n#{answer}"
34
+ end
35
+ end
36
+
37
+ def remember_values
38
+ str = svn(:info, "--revision", revision, repository.url)
39
+ raise Failed, "Could not get 'svn info' from #{repository.url} at revision #{revision}" if str.nil? || str.chomp.strip.empty?
40
+ info = YAML.load(str)
41
+ { Piston::Svn::UUID => info["Repository UUID"],
42
+ Piston::Svn::REMOTE_REV => info["Revision"]}
43
+ end
44
+
45
+ def each
46
+ raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @dir
47
+
48
+ svn(:ls, "--recursive", @dir).each do |relpath|
49
+ next if relpath =~ %r{/$}
50
+ yield relpath.chomp
51
+ end
52
+ end
53
+
54
+ def copy_to(relpath, abspath)
55
+ raise ArgumentError, "Revision #{revision} of #{repository.url} was never checked out -- can't iterate over files" unless @dir
56
+
57
+ Pathname.new(abspath).dirname.mkpath
58
+ FileUtils.cp(@dir + relpath, abspath)
59
+ end
60
+
61
+ def validate!
62
+ data = svn(:info, "--revision", revision, repository.url)
63
+ info = YAML.load(data)
64
+ actual_uuid = info["Repository UUID"]
65
+ raise RepositoryMoved, "Repository at #{repository.url} does not exist anymore:\n#{data}" if actual_uuid.blank?
66
+ raise UuidChanged, "Expected repository at #{repository.url} to have UUID #{recalled_uuid} but found #{actual_uuid}" if recalled_uuid != actual_uuid
67
+ end
68
+
69
+ def recalled_uuid
70
+ recalled_values[Piston::Svn::UUID]
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,108 @@
1
+ require "yaml"
2
+
3
+ module Piston
4
+ module Svn
5
+ class WorkingCopy < Piston::WorkingCopy
6
+ # Register ourselves as a handler for working copies
7
+ Piston::WorkingCopy.add_handler self
8
+
9
+ class << self
10
+ def understands_dir?(dir)
11
+ result = svn(:info, dir) rescue :failed
12
+ result == :failed ? false : true
13
+ end
14
+
15
+ def client
16
+ @@client ||= Piston::Svn::Client.instance
17
+ end
18
+
19
+ def svn(*args)
20
+ client.svn(*args)
21
+ end
22
+ end
23
+
24
+ def svn(*args)
25
+ self.class.svn(*args)
26
+ end
27
+
28
+ def exist?
29
+ result = svn(:info, path) rescue :failed
30
+ logger.debug {"result: #{result.inspect}"}
31
+ return false if result == :failed
32
+ return false if result.nil? || result.chomp.strip.empty?
33
+ return true if YAML.load(result).has_key?("Path")
34
+ end
35
+
36
+ def create
37
+ svn(:mkdir, path)
38
+ end
39
+
40
+ def after_remember(path)
41
+ begin
42
+ info = svn(:info, path)
43
+ rescue Piston::Svn::Client::CommandError
44
+ ensure
45
+ return unless info =~ /\(not a versioned resource\)/i || info =~ /\(is not under version control\)/i || info.blank?
46
+ svn(:add, path)
47
+ end
48
+ end
49
+
50
+ def finalize
51
+ targets = []
52
+ Dir[path + "*"].each do |item|
53
+ svn(:add, item)
54
+ end
55
+ end
56
+
57
+ # Returns all defined externals (recursively) of this WC.
58
+ # Returns a Hash:
59
+ # {"vendor/rails" => {:revision => :head, :url => "http://dev.rubyonrails.org/svn/rails/trunk"},
60
+ # "vendor/plugins/will_paginate" => {:revision => 1234, :url => "http://will_paginate.org/svn/trunk"}}
61
+ def externals
62
+ externals = svn(:proplist, "--recursive", "--verbose")
63
+ return Hash.new if externals.blank?
64
+ returning(Hash.new) do |result|
65
+ YAML.load(externals).each_pair do |dir, props|
66
+ next if props["svn:externals"].blank?
67
+ next unless dir =~ /Properties on '([^']+)'/
68
+ basedir = self.path + $1
69
+ exts = props["svn:externals"]
70
+ exts.split("\n").each do |external|
71
+ data = external.match(/^([^\s]+)\s+(?:(?:-r|--revision)\s*(\d+)\s+)?(.+)$/)
72
+ case data.length
73
+ when 4
74
+ subdir, rev, url = data[1], data[2].nil? ? :head : data[2].to_i, data[3]
75
+ else
76
+ raise SyntaxError, "Could not parse svn:externals on #{basedir}: #{external}"
77
+ end
78
+
79
+ result[basedir + subdir] = {:revision => rev, :url => url}
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def merge_changes(to)
86
+ data = svn(:info, yaml_path)
87
+ info = YAML.load(data)
88
+ initial_revision = info["Last Changed Rev"].to_i
89
+ logger.debug {"Going to merge #{initial_revision} to #{to.revision}"}
90
+ svn(:merge, "--revision", "#{initial_revision}:#{to.revision}", path, path)
91
+ logger.debug {"after merge"}
92
+ end
93
+
94
+ def remove_external_references(*targets)
95
+ svn(:propdel, "svn:externals", *targets)
96
+ end
97
+
98
+ protected
99
+ def do_update(to, lock)
100
+ logger.info "Copying new changes in place"
101
+ copy_from(to)
102
+ logger.info "Merging local changes into working copy"
103
+ merge_changes(to)
104
+ remember(recall.merge(:lock => lock), to.remember_values)
105
+ end
106
+ end
107
+ end
108
+ end