roger 1.1.3 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -0
  3. data/.rubocop.yml +47 -0
  4. data/.travis.yml +1 -5
  5. data/CHANGELOG.md +8 -0
  6. data/Gemfile +3 -3
  7. data/Rakefile +10 -4
  8. data/bin/roger +1 -1
  9. data/doc/mockupfile.md +97 -0
  10. data/doc/templating.md +5 -1
  11. data/examples/default_template/Gemfile +1 -1
  12. data/lib/roger/cli.rb +41 -36
  13. data/lib/roger/cli/command.rb +2 -4
  14. data/lib/roger/cli/generate.rb +1 -0
  15. data/lib/roger/cli/release.rb +2 -2
  16. data/lib/roger/cli/serve.rb +11 -11
  17. data/lib/roger/cli/test.rb +6 -5
  18. data/lib/roger/extractor.rb +42 -43
  19. data/lib/roger/generators.rb +27 -19
  20. data/lib/roger/generators/generator.rb +7 -10
  21. data/lib/roger/generators/new.rb +56 -41
  22. data/lib/roger/generators/templates/generator.tt +5 -5
  23. data/lib/roger/helpers/get_callable.rb +15 -14
  24. data/lib/roger/helpers/logging.rb +35 -13
  25. data/lib/roger/mockupfile.rb +13 -23
  26. data/lib/roger/project.rb +41 -34
  27. data/lib/roger/rack/roger.rb +28 -29
  28. data/lib/roger/rack/sleep.rb +4 -5
  29. data/lib/roger/release.rb +95 -72
  30. data/lib/roger/release/cleaner.rb +14 -13
  31. data/lib/roger/release/finalizers.rb +10 -10
  32. data/lib/roger/release/finalizers/dir.rb +17 -19
  33. data/lib/roger/release/finalizers/git_branch.rb +76 -38
  34. data/lib/roger/release/finalizers/rsync.rb +60 -49
  35. data/lib/roger/release/finalizers/zip.rb +32 -29
  36. data/lib/roger/release/injector.rb +43 -37
  37. data/lib/roger/release/processors.rb +24 -22
  38. data/lib/roger/release/processors/mockup.rb +97 -69
  39. data/lib/roger/release/processors/url_relativizer.rb +57 -30
  40. data/lib/roger/release/scm.rb +30 -27
  41. data/lib/roger/release/scm/git.rb +101 -92
  42. data/lib/roger/resolver.rb +86 -61
  43. data/lib/roger/server.rb +52 -27
  44. data/lib/roger/template.rb +102 -74
  45. data/lib/roger/test.rb +16 -13
  46. data/lib/roger/version.rb +3 -2
  47. data/roger.gemspec +9 -5
  48. data/test/helpers/cli.rb +17 -15
  49. data/test/project/Gemfile +2 -2
  50. data/test/project/html/formats/csv.rcsv +0 -0
  51. data/test/project/lib/generators/test.rb +2 -3
  52. data/test/project/lib/tests/fail/fail.rb +5 -6
  53. data/test/project/lib/tests/noop/lib/cli.rb +2 -1
  54. data/test/project/lib/tests/noop/lib/test.rb +5 -5
  55. data/test/project/lib/tests/noop/noop.rb +2 -1
  56. data/test/project/lib/tests/succeed/succeed.rb +5 -6
  57. data/test/unit/cli/cli_base_test.rb +2 -3
  58. data/test/unit/cli/cli_generate_test.rb +9 -10
  59. data/test/unit/cli/cli_serve_test.rb +22 -18
  60. data/test/unit/cli/cli_test_test.rb +13 -15
  61. data/test/unit/cli/cli_version_test.rb +4 -4
  62. data/test/unit/generators_test.rb +8 -10
  63. data/test/unit/helpers/logging_test.rb +64 -0
  64. data/test/unit/rack/roger_test.rb +21 -0
  65. data/test/unit/release/cleaner_test.rb +23 -19
  66. data/test/unit/release/finalizers/git_branch_test.rb +2 -1
  67. data/test/unit/release/finalizers/zip_test.rb +48 -0
  68. data/test/unit/release/mockup_test.rb +48 -0
  69. data/test/unit/release/processors_test.rb +19 -19
  70. data/test/unit/release_test.rb +15 -14
  71. data/test/unit/resolver_test.rb +21 -14
  72. data/test/unit/server_test.rb +31 -0
  73. data/test/unit/template_test.rb +58 -36
  74. data/test/unit/test_test.rb +3 -2
  75. metadata +35 -9
  76. data/test/Mockupfile-syntax.rb +0 -93
@@ -1,16 +1,18 @@
1
1
  module Roger
2
+ # The cleaner safely cleans up paths
2
3
  class Release::Cleaner
3
4
  def initialize(pattern)
4
5
  @pattern = [pattern].flatten
5
6
  end
6
-
7
- def call(release, options = {})
8
- # We switch to the build path and append the globbed files for safety, so even if you manage to sneak in a
9
- # pattern like "/**/*" it won't do you any good as it will be reappended to the path
7
+
8
+ # We switch to the build path and append the globbed files for safety, so even if you manage
9
+ # to sneak in a pattern like "/**/*" it won't do you any good as it will be reappended
10
+ # to the path
11
+ def call(release, _options = {})
10
12
  Dir.chdir(release.build_path.to_s) do
11
13
  @pattern.each do |pattern|
12
14
  Dir.glob(pattern).each do |file|
13
- self.clean_path(release, file)
15
+ clean_path(release, file)
14
16
  end
15
17
  end
16
18
  end
@@ -18,7 +20,7 @@ module Roger
18
20
 
19
21
  def clean_path(release, file)
20
22
  path = File.join(release.build_path.to_s, file)
21
- if is_inside_build_path(release.build_path, path)
23
+ if inside_build_path?(release.build_path, path)
22
24
  release.log(self, "Cleaning up \"#{path}\" in build")
23
25
  rm_rf(path)
24
26
  true
@@ -30,12 +32,11 @@ module Roger
30
32
 
31
33
  protected
32
34
 
33
- def is_inside_build_path(build_path, path)
34
-
35
- begin
35
+ def inside_build_path?(build_path, path)
36
+ begin
36
37
  build_path = Pathname.new(build_path).realpath.to_s
37
38
  path = Pathname.new(path)
38
- if(path.absolute?)
39
+ if path.absolute?
39
40
  path = path.realpath.to_s
40
41
  else
41
42
  path = Pathname.new(File.join(build_path.to_s, path)).realpath.to_s
@@ -44,12 +45,12 @@ module Roger
44
45
  # Real path does not exist
45
46
  return false
46
47
  end
47
-
48
+
48
49
  if path[build_path]
49
50
  return true
50
51
  else
51
- raise RuntimeError, "Cleaning pattern is not inside build directory"
52
+ fail "Cleaning pattern is not inside build directory"
52
53
  end
53
54
  end
54
55
  end
55
- end
56
+ end
@@ -1,30 +1,30 @@
1
+ # The Finalizers will finalize the release. Finalizers can be used to
2
+ # copy the release, zip the release or upload the release
1
3
  module Roger::Release::Finalizers
2
- class Base
3
-
4
+ # Abstract base finalizer
5
+ class Base
4
6
  def initialize(options = {})
5
7
  @options = {}
6
8
  @options.update(options) if options
7
9
  end
8
-
9
- def call(release, options = {})
10
- raise ArgumentError, "Implement in subclass"
10
+
11
+ def call(_release, _options = {})
12
+ fail ArgumentError, "Implement in subclass"
11
13
  end
12
14
  end
13
15
 
14
16
  def self.register(name, finalizer)
15
- raise ArgumentError, "Another finalizer has already claimed the name #{name.inspect}" if self.map.has_key?(name)
16
- raise ArgumentError, "Name must be a symbol" unless name.kind_of?(Symbol)
17
- self.map[name] = finalizer
17
+ fail ArgumentError, "Finalizer name '#{name.inspect}' already in use" if map.key?(name)
18
+ fail ArgumentError, "Name must be a symbol" unless name.is_a?(Symbol)
19
+ map[name] = finalizer
18
20
  end
19
21
 
20
22
  def self.map
21
23
  @_map ||= {}
22
24
  end
23
-
24
25
  end
25
26
 
26
27
  require File.dirname(__FILE__) + "/finalizers/zip"
27
28
  require File.dirname(__FILE__) + "/finalizers/dir"
28
29
  require File.dirname(__FILE__) + "/finalizers/rsync"
29
30
  require File.dirname(__FILE__) + "/finalizers/git_branch"
30
-
@@ -1,31 +1,29 @@
1
- require 'fileutils'
1
+ require "fileutils"
2
2
 
3
3
  module Roger::Release::Finalizers
4
-
5
4
  # Finalizes the release into a directory in target_path
6
- #
5
+ #
7
6
  # The directory name will have the format PREFIX-VERSION
8
- #
9
- class Dir < Base
7
+ #
8
+ class Dir < Base
10
9
  # @option options :prefix Prefix to put before the version (default = "html")
11
10
  def call(release, options = {})
12
- if options
13
- options = @options.dup.update(options)
14
- else
15
- options = @options
16
- end
11
+ options = {}.update(@options)
12
+ options.update(options) if options
13
+
14
+ name = [(options[:prefix] || "html"), release.scm.version].join("-")
15
+ target_path = release.target_path + name
17
16
 
18
- name = [(options[:prefix] || "html"), release.scm.version].join("-")
19
- release.log(self, "Finalizing release to #{release.target_path + name}")
20
-
21
- if File.exist?(release.target_path + name)
22
- release.log(self, "Removing existing target #{release.target_path + name}")
23
- FileUtils.rm_rf(release.target_path + name)
17
+ release.log(self, "Finalizing release to #{target_path}")
18
+
19
+ if File.exist?(target_path)
20
+ release.log(self, "Removing existing target #{target_path}")
21
+ FileUtils.rm_rf(target_path)
24
22
  end
25
-
26
- FileUtils.cp_r release.build_path, release.target_path + name
23
+
24
+ FileUtils.cp_r release.build_path, target_path
27
25
  end
28
26
  end
29
27
  end
30
28
 
31
- Roger::Release::Finalizers.register(:dir, Roger::Release::Finalizers::Dir)
29
+ Roger::Release::Finalizers.register(:dir, Roger::Release::Finalizers::Dir)
@@ -23,81 +23,119 @@ module Roger::Release::Finalizers
23
23
  def call(release, options = {})
24
24
  @options = @options.dup.update(options)
25
25
  remote = find_git_remote
26
-
27
- # 0. Get remote
28
- unless remote
29
- raise "No remote found for origin"
30
- end
31
-
32
- e_remote = Shellwords.escape(remote)
33
- e_branch = Shellwords.escape(@options[:branch])
26
+ branch = @options[:branch]
34
27
 
35
28
  tmp_dir = Pathname.new(::Dir.mktmpdir)
36
29
  clone_dir = tmp_dir + "clone"
37
30
 
38
31
  # Check if remote already has branch
39
- if `git ls-remote --heads #{e_remote} refs/heads/#{e_branch}` == ""
40
- release.log(self, "Creating empty branch")
41
- # Branch does not exist yet
42
- FileUtils.mkdir(clone_dir)
43
- ::Dir.chdir(clone_dir) do
44
- `git init`
45
- `git remote add origin #{e_remote}`
46
- `git checkout -b #{e_branch}`
47
- end
48
- else
32
+ if remote_has_branch?(remote, branch)
49
33
  release.log(self, "Cloning existing repo")
50
- clone_options = "--branch #{e_branch} --single-branch #{clone_dir}"
51
- # 1. Clone into different directory
52
- `git clone #{e_remote} #{clone_options}`
34
+ clone_branch(clone_dir, remote, branch)
35
+ else
36
+ release.log(self, "Creating empty branch")
37
+ create_empty_branch(clone_dir, remote, branch)
53
38
  end
54
39
 
55
40
  release.log(self, "Working git magic in #{clone_dir}")
41
+
42
+ commit_and_push_release(clone_dir, release, @options)
43
+
44
+ if @options[:cleanup]
45
+ FileUtils.rm_rf(tmp_dir)
46
+ else
47
+ tmp_dir
48
+ end
49
+ end
50
+
51
+ protected
52
+
53
+ def commit_and_push_release(clone_dir, release, options)
56
54
  ::Dir.chdir(clone_dir) do
57
55
  # 3. Copy changes
58
56
  FileUtils.rm_rf("*")
59
57
  FileUtils.cp_r release.build_path.to_s + "/.", clone_dir.to_s
60
58
 
61
- # 4. Add all files
62
- `git add .`
59
+ commands = [
60
+ %w(git add .), # 4. Add all files
61
+ %w(git commit -a -m) << "Release #{release.scm.version}" # 5. Commit
62
+ ]
63
63
 
64
- # 5. Commit
65
- `git commit -a -m "Release #{release.scm.version}"`
64
+ # 6. Git push if in options
65
+ commands << (%w(git push origin) << branch) if options[:push]
66
66
 
67
- # 6. Git push
68
- if @options[:push]
69
- `git push origin #{e_branch}`
67
+ commands.each do |command|
68
+ `#{Shellwords.join(command)}`
70
69
  end
71
70
  end
71
+ end
72
72
 
73
- if @options[:cleanup]
74
- FileUtils.rm_rf(tmp_dir)
75
- else
76
- tmp_dir
73
+ # Check if remote already has branch
74
+ def remote_has_branch?(remote, branch)
75
+ command = Shellwords.join([
76
+ "git",
77
+ "ls-remote",
78
+ "--heads",
79
+ remote,
80
+ "refs/heads/#{branch}"
81
+ ])
82
+ `#{command}` != ""
83
+ end
84
+
85
+ def create_empty_branch(clone_dir, remote, branch)
86
+ commands = [
87
+ %w(git init),
88
+ %w(git remote add origin) << remote,
89
+ %w(git checkout -b) << branch
90
+ ]
91
+
92
+ # Branch does not exist yet
93
+ FileUtils.mkdir(clone_dir)
94
+ ::Dir.chdir(clone_dir) do
95
+ commands.each do |command|
96
+ `#{Shellwords.join(command)}`
97
+ end
77
98
  end
78
99
  end
79
100
 
80
- protected
101
+ def clone_branch(clone_dir, remote, branch)
102
+ command = Shellwords.join([
103
+ "git",
104
+ "clone",
105
+ remote,
106
+ "--branch",
107
+ branch,
108
+ "--single-branch",
109
+ clone_dir
110
+ ])
111
+ `#{command}`
112
+ end
81
113
 
82
114
  # Find the git dir
83
- # TODO this is just a copy from release/scm/git.rb
115
+ # TODO: this is just a copy from release/scm/git.rb
84
116
  def find_git_dir(path)
85
117
  path = Pathname.new(path).realpath
86
118
  while path.parent != path && !(path + ".git").directory?
87
119
  path = path.parent
88
120
  end
89
121
 
90
- path = path + ".git"
122
+ path += ".git"
91
123
 
92
- raise "Could not find suitable .git dir in #{path}" if !path.directory?
124
+ fail "Could not find suitable .git dir in #{path}" unless path.directory?
93
125
 
94
126
  path
95
127
  end
96
128
 
97
129
  def find_git_remote
98
- (@options[:remote] ||
130
+ remote =
131
+ @options[:remote] ||
99
132
  `git --git-dir=#{find_git_dir} config --get remote.origin.url`
100
- ).strip
133
+
134
+ remote.strip!
135
+
136
+ fail "No remote found for origin" if remote.nil? || remote.empty?
137
+
138
+ remote
101
139
  end
102
140
  end
103
141
  end
@@ -1,13 +1,11 @@
1
- require 'shellwords'
1
+ require "shellwords"
2
2
 
3
3
  module Roger::Release::Finalizers
4
-
5
4
  # Finalizes the release by uploading your mockup with rsync to a remote server
6
- #
5
+ #
7
6
  # @see RsyncFinalizer#initialize for options
8
7
  #
9
8
  class Rsync < Base
10
-
11
9
  # @param Hash options The options
12
10
  #
13
11
  # @option options String :rsync The Rsync command to run (default is "rsync")
@@ -17,63 +15,76 @@ module Roger::Release::Finalizers
17
15
  # @option options Boolean :ask Prompt the user before uploading (default is true)
18
16
  def initialize(options = {})
19
17
  @options = {
20
- :rsync => "rsync",
21
- :remote_path => "",
22
- :host => "",
23
- :username => "",
24
- :ask => true
18
+ rsync: "rsync",
19
+ remote_path: "",
20
+ host: "",
21
+ username: "",
22
+ ask: true
25
23
  }.update(options)
26
24
  end
27
-
25
+
28
26
  def call(release, options = {})
29
27
  options = @options.dup.update(options)
30
-
28
+
31
29
  # Validate options
32
30
  validate_options!(release, options)
33
-
34
- if !options[:ask] || (prompt("Do you wish to upload to #{options[:host]}? [y/N]: ")) =~ /\Ay(es)?\Z/
35
- begin
36
- `#{@options[:rsync]} --version`
37
- rescue Errno::ENOENT
38
- raise RuntimeError, "Could not find rsync in #{@options[:rsync].inspect}"
39
- end
40
-
41
- local_path = release.build_path.to_s
42
- remote_path = options[:remote_path]
43
-
44
- local_path += "/" unless local_path =~ /\/\Z/
45
- remote_path += "/" unless remote_path =~ /\/\Z/
46
-
47
- release.log(self, "Starting upload of #{(release.build_path + "*")} to #{options[:host]}")
48
-
49
- command = "#{options[:rsync]} -az #{Shellwords.escape(local_path)} #{Shellwords.escape(options[:username])}@#{Shellwords.escape(options[:host])}:#{Shellwords.escape(remote_path)}"
50
-
51
- # Run r.js optimizer
52
- output = `#{command}`
53
-
54
- # Check if r.js succeeded
55
- unless $?.success?
56
- raise RuntimeError, "Rsync failed.\noutput:\n #{output}"
57
- end
58
- end
31
+
32
+ return unless prompt_for_upload(options)
33
+
34
+ check_rsync_command(options[:rsync])
35
+
36
+ local_path = release.build_path.to_s
37
+ remote_path = options[:remote_path]
38
+
39
+ local_path += "/" unless local_path =~ %r{/\Z}
40
+ remote_path += "/" unless remote_path =~ %r{/\Z}
41
+
42
+ release.log(self, "Starting upload of #{(release.build_path + '*')} to #{options[:host]}")
43
+ rsync(options[:rsync], local_path, remote_path, options)
59
44
  end
60
-
45
+
61
46
  protected
62
-
47
+
48
+ def check_rsync_command(command)
49
+ `#{command} --version`
50
+ rescue Errno::ENOENT
51
+ raise "Could not find rsync in #{command.inspect}"
52
+ end
53
+
54
+ def rsync(command, local_path, remote_path, options = {})
55
+ target_path = "#{options[:username]}@#{options[:host]}:#{remote_path}"
56
+
57
+ command = [
58
+ options[:rsync],
59
+ "-az",
60
+ Shellwords.escape(local_path),
61
+ Shellwords.escape(target_path)
62
+ ]
63
+
64
+ # Run rsync
65
+ output = `#{command.join(" ")}`
66
+
67
+ # Check if rsync succeeded
68
+ fail "Rsync failed.\noutput:\n #{output}" unless $CHILD_STATUS.success?
69
+ end
70
+
71
+ def prompt_for_upload(options)
72
+ !options[:ask] ||
73
+ (prompt("Do you wish to upload to #{options[:host]}? [y/N]: ")) =~ /\Ay(es)?\Z/
74
+ end
75
+
63
76
  def validate_options!(release, options)
64
77
  must_have_keys = [:remote_path, :host, :username]
65
- if (options.keys & must_have_keys).size != must_have_keys.size
66
- release.log(self, "You must specify these options: #{(must_have_keys - options.keys).inspect}")
67
- raise "Missing keys: #{(must_have_keys - options.keys).inspect}"
68
- end
78
+ return if (options.keys & must_have_keys).size == must_have_keys.size
79
+
80
+ release.log(self, "Missing options: #{(must_have_keys - options.keys).inspect}")
81
+ fail "Missing keys: #{(must_have_keys - options.keys).inspect}"
69
82
  end
70
-
71
- def prompt(question = 'Do you wish to continue?')
83
+
84
+ def prompt(question = "Do you wish to continue?")
72
85
  print(question)
73
- return $stdin.gets.strip
86
+ $stdin.gets.strip
74
87
  end
75
-
76
88
  end
77
89
  end
78
-
79
- Roger::Release::Finalizers.register(:rsync, Roger::Release::Finalizers::Rsync)
90
+ Roger::Release::Finalizers.register(:rsync, Roger::Release::Finalizers::Rsync)