jeremylightsmith-piston 1.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.gitignore +6 -0
  2. data/History.txt +6 -0
  3. data/License.txt +20 -0
  4. data/Manifest.txt +86 -0
  5. data/README.txt +136 -0
  6. data/Rakefile +4 -0
  7. data/bin/piston +5 -0
  8. data/config/hoe.rb +76 -0
  9. data/config/requirements.rb +18 -0
  10. data/lib/piston.rb +18 -0
  11. data/lib/piston/cli.rb +279 -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 +69 -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 +28 -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 +70 -0
  21. data/lib/piston/git/repository.rb +63 -0
  22. data/lib/piston/git/working_copy.rb +63 -0
  23. data/lib/piston/repository.rb +57 -0
  24. data/lib/piston/revision.rb +48 -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 +55 -0
  30. data/lib/piston/version.rb +9 -0
  31. data/lib/piston/working_copy.rb +129 -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 +40 -0
  44. data/tasks/environment.rake +7 -0
  45. data/tasks/samples.rake +6 -0
  46. data/tasks/test.rake +30 -0
  47. data/tasks/website.rake +17 -0
  48. data/test/integration/test_import_git_git.rb +96 -0
  49. data/test/integration/test_import_git_svn.rb +131 -0
  50. data/test/integration/test_import_svn_git.rb +52 -0
  51. data/test/integration/test_import_svn_svn.rb +43 -0
  52. data/test/integration_helpers.rb +33 -0
  53. data/test/test_helper.rb +56 -0
  54. data/test/unit/git/commit/test_checkout.rb +30 -0
  55. data/test/unit/git/commit/test_each.rb +36 -0
  56. data/test/unit/git/commit/test_rememberance.rb +20 -0
  57. data/test/unit/git/commit/test_validation.rb +33 -0
  58. data/test/unit/git/repository/test_at.rb +22 -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 +28 -0
  63. data/test/unit/git/working_copy/test_creation.rb +26 -0
  64. data/test/unit/git/working_copy/test_existence.rb +21 -0
  65. data/test/unit/git/working_copy/test_finalization.rb +18 -0
  66. data/test/unit/git/working_copy/test_guessing.rb +34 -0
  67. data/test/unit/git/working_copy/test_rememberance.rb +25 -0
  68. data/test/unit/svn/repository/test_at.rb +18 -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 +27 -0
  72. data/test/unit/svn/revision/test_each.rb +21 -0
  73. data/test/unit/svn/revision/test_rememberance.rb +42 -0
  74. data/test/unit/svn/revision/test_validation.rb +49 -0
  75. data/test/unit/svn/working_copy/test_copying.rb +29 -0
  76. data/test/unit/svn/working_copy/test_creation.rb +19 -0
  77. data/test/unit/svn/working_copy/test_existence.rb +26 -0
  78. data/test/unit/svn/working_copy/test_finalization.rb +21 -0
  79. data/test/unit/svn/working_copy/test_guessing.rb +17 -0
  80. data/test/unit/svn/working_copy/test_rememberance.rb +26 -0
  81. data/test/unit/test_import.rb +13 -0
  82. data/test/unit/test_info.rb +36 -0
  83. data/test/unit/test_lock_unlock.rb +46 -0
  84. data/test/unit/test_repository.rb +50 -0
  85. data/test/unit/test_revision.rb +30 -0
  86. data/test/unit/working_copy/test_guessing.rb +34 -0
  87. data/test/unit/working_copy/test_info.rb +13 -0
  88. data/test/unit/working_copy/test_rememberance.rb +48 -0
  89. data/test/unit/working_copy/test_validate.rb +68 -0
  90. data/website/index.html +11 -0
  91. data/website/index.txt +39 -0
  92. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  93. data/website/stylesheets/screen.css +138 -0
  94. data/website/template.rhtml +48 -0
  95. metadata +228 -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,48 @@
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
12
+
13
+ def initialize(repository, revision, recalled_values={})
14
+ @repository, @revision, @recalled_values = repository, revision, recalled_values
15
+ end
16
+
17
+ def name
18
+ @revision
19
+ end
20
+
21
+ def logger
22
+ self.class.logger
23
+ end
24
+
25
+ def to_s
26
+ "Piston::Revision(#{@repository.url}@#{@revision})"
27
+ end
28
+
29
+ # Retrieve a copy of this repository into +dir+.
30
+ def checkout_to(dir)
31
+ logger.debug {"Checking out #{@repository}@#{@revision} into #{dir}"}
32
+ end
33
+
34
+ # What values does this revision want to remember for the future ?
35
+ def remember_values
36
+ logger.debug {"Generating remember values"}
37
+ {}
38
+ end
39
+
40
+ # Yields each file of this revision in turn to our caller.
41
+ def each
42
+ end
43
+
44
+ # Copies +relpath+ (relative to ourselves) to +abspath+ (an absolute path).
45
+ def copy_to(relpath, abspath)
46
+ end
47
+ end
48
+ end
@@ -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(path)
24
+ @wcpath = path.kind_of?(Pathname) ? path : Pathname.new(path)
25
+ answer = svn(:checkout, "--revision", revision, repository.url, path)
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 #{path}\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 @wcpath
47
+
48
+ svn(:ls, "--recursive", @wcpath).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 @wcpath
56
+
57
+ Pathname.new(abspath).dirname.mkpath
58
+ FileUtils.cp(@wcpath + 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,55 @@
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
+ info = svn(:info, path)
42
+ return unless info =~ /\(not a versioned resource\)/i || info.empty?
43
+ svn(:add, path)
44
+ end
45
+
46
+ def finalize
47
+ targets = []
48
+ Dir[path + "*"].each do |item|
49
+ svn(:add, item)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,9 @@
1
+ module Piston #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 9
5
+ TINY = 3
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join(".")
8
+ end
9
+ end
@@ -0,0 +1,129 @@
1
+ module Piston
2
+ class WorkingCopy
3
+ class UnhandledWorkingCopy < RuntimeError; end
4
+ class NotWorkingCopy < RuntimeError; end
5
+
6
+ class << self
7
+ def logger
8
+ @@logger ||= Log4r::Logger["handler"]
9
+ end
10
+
11
+ def guess(path)
12
+ path = path.kind_of?(Pathname) ? path : Pathname.new(path.to_s)
13
+ logger.info {"Guessing the working copy type of #{path.inspect}"}
14
+ handler = handlers.detect do |handler|
15
+ logger.debug {"Asking #{handler.name} if it understands #{path}"}
16
+ handler.understands_dir?(path)
17
+ end
18
+
19
+ raise UnhandledWorkingCopy, "Don't know what working copy type #{path} is." if handler.nil?
20
+ handler.new(path)
21
+ end
22
+
23
+ @@handlers = Array.new
24
+ def add_handler(handler)
25
+ @@handlers << handler
26
+ end
27
+
28
+ def handlers
29
+ @@handlers
30
+ end
31
+ private :handlers
32
+ end
33
+
34
+ attr_reader :path
35
+
36
+ def initialize(path)
37
+ @path = path.kind_of?(Pathname) ? path : Pathname.new(path)
38
+ logger.debug {"Initialized on #{@path}"}
39
+ end
40
+
41
+ def logger
42
+ self.class.logger
43
+ end
44
+
45
+ def to_s
46
+ "Piston::WorkingCopy(#{@path})"
47
+ end
48
+
49
+ def exist?
50
+ @path.exist? && @path.directory?
51
+ end
52
+
53
+ def pistonized?
54
+ yaml_path.exist? && yaml_path.file?
55
+ end
56
+
57
+ def validate!
58
+ raise NotWorkingCopy unless self.pistonized?
59
+ self
60
+ end
61
+
62
+ # Creates the initial working copy for pistonizing a new repository.
63
+ def create
64
+ logger.debug {"Creating working copy at #{path}"}
65
+ end
66
+
67
+ # Copy files from +revision+. +revision+ must
68
+ # #respond_to?(:each), and return each file that is to be copied.
69
+ # Only files must be returned.
70
+ #
71
+ # Each item yielded by Revision#each must be a relative path.
72
+ #
73
+ # WorkingCopy will call Revision#copy_to with the full path to where the
74
+ # file needs to be copied.
75
+ def copy_from(revision)
76
+ revision.each do |relpath|
77
+ target = path + relpath
78
+ target.dirname.mkdir rescue nil
79
+
80
+ logger.debug {"Copying #{relpath} to #{target}"}
81
+ revision.copy_to(relpath, target)
82
+ end
83
+ end
84
+
85
+ # Stores a Hash of values that can be retrieved later.
86
+ def remember(values, handler_values)
87
+ values["format"] = 1
88
+
89
+ # Stringify keys
90
+ values.keys.each do |key|
91
+ values[key.to_s] = values.delete(key)
92
+ end
93
+
94
+ logger.debug {"Remembering #{values.inspect} as well as #{handler_values.inspect}"}
95
+ File.open(yaml_path, "wb") do |f|
96
+ f.write(values.merge("handler" => handler_values).to_yaml)
97
+ end
98
+
99
+ logger.debug {"Calling \#after_remember on #{yaml_path}"}
100
+ after_remember(yaml_path)
101
+ end
102
+
103
+ # Callback after #remember is done, to do whatever the
104
+ # working copy needs to do with the file.
105
+ def after_remember(path)
106
+ end
107
+
108
+ # Recalls a Hash of values from the working copy.
109
+ def recall
110
+ YAML.load(File.read(yaml_path))
111
+ end
112
+
113
+ def finalize
114
+ logger.debug {"Finalizing #{path}"}
115
+ end
116
+
117
+ # Returns basic information about this working copy.
118
+ def info
119
+ recall
120
+ end
121
+
122
+
123
+ protected
124
+ # The path to the piston YAML file.
125
+ def yaml_path
126
+ path + ".piston.yml"
127
+ end
128
+ end
129
+ end