roger 1.1.3 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|