roger 1.1.3 → 1.2.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 (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)