jeremylightsmith-piston 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 (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