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.
- checksums.yaml +4 -4
- data/.hound.yml +2 -0
- data/.rubocop.yml +47 -0
- data/.travis.yml +1 -5
- data/CHANGELOG.md +8 -0
- data/Gemfile +3 -3
- data/Rakefile +10 -4
- data/bin/roger +1 -1
- data/doc/mockupfile.md +97 -0
- data/doc/templating.md +5 -1
- data/examples/default_template/Gemfile +1 -1
- data/lib/roger/cli.rb +41 -36
- data/lib/roger/cli/command.rb +2 -4
- data/lib/roger/cli/generate.rb +1 -0
- data/lib/roger/cli/release.rb +2 -2
- data/lib/roger/cli/serve.rb +11 -11
- data/lib/roger/cli/test.rb +6 -5
- data/lib/roger/extractor.rb +42 -43
- data/lib/roger/generators.rb +27 -19
- data/lib/roger/generators/generator.rb +7 -10
- data/lib/roger/generators/new.rb +56 -41
- data/lib/roger/generators/templates/generator.tt +5 -5
- data/lib/roger/helpers/get_callable.rb +15 -14
- data/lib/roger/helpers/logging.rb +35 -13
- data/lib/roger/mockupfile.rb +13 -23
- data/lib/roger/project.rb +41 -34
- data/lib/roger/rack/roger.rb +28 -29
- data/lib/roger/rack/sleep.rb +4 -5
- data/lib/roger/release.rb +95 -72
- data/lib/roger/release/cleaner.rb +14 -13
- data/lib/roger/release/finalizers.rb +10 -10
- data/lib/roger/release/finalizers/dir.rb +17 -19
- data/lib/roger/release/finalizers/git_branch.rb +76 -38
- data/lib/roger/release/finalizers/rsync.rb +60 -49
- data/lib/roger/release/finalizers/zip.rb +32 -29
- data/lib/roger/release/injector.rb +43 -37
- data/lib/roger/release/processors.rb +24 -22
- data/lib/roger/release/processors/mockup.rb +97 -69
- data/lib/roger/release/processors/url_relativizer.rb +57 -30
- data/lib/roger/release/scm.rb +30 -27
- data/lib/roger/release/scm/git.rb +101 -92
- data/lib/roger/resolver.rb +86 -61
- data/lib/roger/server.rb +52 -27
- data/lib/roger/template.rb +102 -74
- data/lib/roger/test.rb +16 -13
- data/lib/roger/version.rb +3 -2
- data/roger.gemspec +9 -5
- data/test/helpers/cli.rb +17 -15
- data/test/project/Gemfile +2 -2
- data/test/project/html/formats/csv.rcsv +0 -0
- data/test/project/lib/generators/test.rb +2 -3
- data/test/project/lib/tests/fail/fail.rb +5 -6
- data/test/project/lib/tests/noop/lib/cli.rb +2 -1
- data/test/project/lib/tests/noop/lib/test.rb +5 -5
- data/test/project/lib/tests/noop/noop.rb +2 -1
- data/test/project/lib/tests/succeed/succeed.rb +5 -6
- data/test/unit/cli/cli_base_test.rb +2 -3
- data/test/unit/cli/cli_generate_test.rb +9 -10
- data/test/unit/cli/cli_serve_test.rb +22 -18
- data/test/unit/cli/cli_test_test.rb +13 -15
- data/test/unit/cli/cli_version_test.rb +4 -4
- data/test/unit/generators_test.rb +8 -10
- data/test/unit/helpers/logging_test.rb +64 -0
- data/test/unit/rack/roger_test.rb +21 -0
- data/test/unit/release/cleaner_test.rb +23 -19
- data/test/unit/release/finalizers/git_branch_test.rb +2 -1
- data/test/unit/release/finalizers/zip_test.rb +48 -0
- data/test/unit/release/mockup_test.rb +48 -0
- data/test/unit/release/processors_test.rb +19 -19
- data/test/unit/release_test.rb +15 -14
- data/test/unit/resolver_test.rb +21 -14
- data/test/unit/server_test.rb +31 -0
- data/test/unit/template_test.rb +58 -36
- data/test/unit/test_test.rb +3 -2
- metadata +35 -9
- 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
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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(
|
10
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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,
|
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
|
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
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
62
|
-
|
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
|
-
#
|
65
|
-
|
64
|
+
# 6. Git push if in options
|
65
|
+
commands << (%w(git push origin) << branch) if options[:push]
|
66
66
|
|
67
|
-
|
68
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
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
|
122
|
+
path += ".git"
|
91
123
|
|
92
|
-
|
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
|
-
|
130
|
+
remote =
|
131
|
+
@options[:remote] ||
|
99
132
|
`git --git-dir=#{find_git_dir} config --get remote.origin.url`
|
100
|
-
|
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
|
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
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
66
|
-
|
67
|
-
|
68
|
-
|
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 =
|
83
|
+
|
84
|
+
def prompt(question = "Do you wish to continue?")
|
72
85
|
print(question)
|
73
|
-
|
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)
|