roger 0.0.1 → 0.10.0
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 +8 -8
- data/.gitignore +2 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +102 -0
- data/Gemfile +5 -0
- data/MIT_LICENSE +20 -0
- data/README.md +10 -10
- data/Rakefile +9 -0
- data/bin/roger +5 -0
- data/doc/cli.md +46 -0
- data/doc/mockupfile.md +3 -0
- data/doc/templating.md +88 -0
- data/examples/default_template/.gitignore +2 -0
- data/examples/default_template/CHANGELOG +0 -0
- data/examples/default_template/Gemfile +3 -0
- data/examples/default_template/Mockupfile +1 -0
- data/examples/default_template/html/.empty_directory +0 -0
- data/examples/default_template/partials/.empty_directory +0 -0
- data/lib/roger/cli/command.rb +23 -0
- data/lib/roger/cli/generate.rb +5 -0
- data/lib/roger/cli/release.rb +10 -0
- data/lib/roger/cli/serve.rb +29 -0
- data/lib/roger/cli.rb +123 -0
- data/lib/roger/extractor.rb +95 -0
- data/lib/roger/generators/generator.rb +23 -0
- data/lib/roger/generators/new.rb +67 -0
- data/lib/roger/generators/templates/generator.tt +13 -0
- data/lib/roger/generators.rb +23 -0
- data/lib/roger/mockupfile.rb +63 -0
- data/lib/roger/project.rb +92 -0
- data/lib/roger/rack/html_validator.rb +26 -0
- data/lib/roger/rack/roger.rb +52 -0
- data/lib/roger/rack/sleep.rb +21 -0
- data/lib/roger/release/cleaner.rb +47 -0
- data/lib/roger/release/finalizers/dir.rb +29 -0
- data/lib/roger/release/finalizers/git_branch.rb +92 -0
- data/lib/roger/release/finalizers/rsync.rb +77 -0
- data/lib/roger/release/finalizers/zip.rb +42 -0
- data/lib/roger/release/finalizers.rb +19 -0
- data/lib/roger/release/injector.rb +99 -0
- data/lib/roger/release/processors/mockup.rb +93 -0
- data/lib/roger/release/processors/url_relativizer.rb +45 -0
- data/lib/roger/release/processors.rb +17 -0
- data/lib/roger/release/scm/git.rb +101 -0
- data/lib/roger/release/scm.rb +32 -0
- data/lib/roger/release.rb +363 -0
- data/lib/roger/resolver.rb +119 -0
- data/lib/roger/server.rb +117 -0
- data/lib/roger/template.rb +206 -0
- data/lib/roger/w3c_validator.rb +129 -0
- data/roger.gemspec +35 -0
- data/test/Mockupfile-syntax.rb +85 -0
- data/test/project/.rvmrc +1 -0
- data/test/project/Gemfile +7 -0
- data/test/project/Gemfile.lock +38 -0
- data/test/project/Mockupfile +13 -0
- data/test/project/html/formats/erb.html.erb +5 -0
- data/test/project/html/formats/index.html +1 -0
- data/test/project/html/formats/json.json.erb +0 -0
- data/test/project/html/formats/markdown.md +3 -0
- data/test/project/html/formats/mockup.html +5 -0
- data/test/project/html/front_matter/erb.html.erb +16 -0
- data/test/project/html/front_matter/markdown.md +7 -0
- data/test/project/html/layouts/content-for.html.erb +17 -0
- data/test/project/html/layouts/erb.html.erb +19 -0
- data/test/project/html/mockup/encoding.html +3 -0
- data/test/project/html/partials/erb.html.erb +10 -0
- data/test/project/html/partials/load_path.html.erb +3 -0
- data/test/project/html/partials/mockup.html +13 -0
- data/test/project/html/static/non-relative.html.erb +9 -0
- data/test/project/html/static/relative.html.erb +9 -0
- data/test/project/layouts/test.html.erb +34 -0
- data/test/project/layouts/yield.html.erb +1 -0
- data/test/project/lib/generators/test.rb +9 -0
- data/test/project/partials/formats/erb.html.erb +1 -0
- data/test/project/partials/partials-test.html.erb +1 -0
- data/test/project/partials/test/erb.html.erb +1 -0
- data/test/project/partials/test/front_matter.html.erb +1 -0
- data/test/project/partials/test/json.json.erb +1 -0
- data/test/project/partials/test/markdown.md +1 -0
- data/test/project/partials/test/mockup.part.html +1 -0
- data/test/project/partials/test/simple.html.erb +1 -0
- data/test/project/partials2/partials2-test.html.erb +1 -0
- data/test/unit/cli_test.rb +12 -0
- data/test/unit/generators_test.rb +75 -0
- data/test/unit/release/cleaner_test.rb +47 -0
- data/test/unit/resolver_test.rb +92 -0
- data/test/unit/template_test.rb +127 -0
- metadata +202 -8
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Roger::Release::Finalizers
|
4
|
+
|
5
|
+
# Finalizes the release by uploading your mockup with rsync to a remote server
|
6
|
+
#
|
7
|
+
# @see RsyncFinalizer#initialize for options
|
8
|
+
#
|
9
|
+
class Rsync < Base
|
10
|
+
|
11
|
+
# @param Hash options The options
|
12
|
+
#
|
13
|
+
# @option options String :rsync The Rsync command to run (default is "rsync")
|
14
|
+
# @option options String :remote_path The remote path to upload to
|
15
|
+
# @option options String :host The remote host to upload to
|
16
|
+
# @option options String :username The remote username to upload to
|
17
|
+
# @option options Boolean :ask Prompt the user before uploading (default is true)
|
18
|
+
def initialize(options = {})
|
19
|
+
@options = {
|
20
|
+
:rsync => "rsync",
|
21
|
+
:remote_path => "",
|
22
|
+
:host => "",
|
23
|
+
:username => "",
|
24
|
+
:ask => true
|
25
|
+
}.update(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(release, options = {})
|
29
|
+
options = @options.dup.update(options)
|
30
|
+
|
31
|
+
# Validate options
|
32
|
+
validate_options!(release, options)
|
33
|
+
|
34
|
+
if !options[:ask] || (prompt("Do you wish to upload to #{options[:host]}? Type y[es]: ")) =~ /\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
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def validate_options!(release, options)
|
64
|
+
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
|
69
|
+
end
|
70
|
+
|
71
|
+
def prompt(question = 'Do you wish to continue?')
|
72
|
+
print(question)
|
73
|
+
return $stdin.gets.strip
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Roger::Release::Finalizers
|
2
|
+
|
3
|
+
class Zip < Base
|
4
|
+
|
5
|
+
attr_reader :release
|
6
|
+
|
7
|
+
# @option options :prefix Prefix to put before the version (default = "html")
|
8
|
+
# @option options :zip The zip command
|
9
|
+
def call(release, options = {})
|
10
|
+
if options
|
11
|
+
options = @options.dup.update(options)
|
12
|
+
else
|
13
|
+
options = @options
|
14
|
+
end
|
15
|
+
|
16
|
+
options = {
|
17
|
+
:zip => "zip",
|
18
|
+
:prefix => "html"
|
19
|
+
}.update(options)
|
20
|
+
|
21
|
+
name = [options[:prefix], release.scm.version].join("-") + ".zip"
|
22
|
+
release.log(self, "Finalizing release to #{release.target_path + name}")
|
23
|
+
|
24
|
+
if File.exist?(release.target_path + name)
|
25
|
+
release.log(self, "Removing existing target #{release.target_path + name}")
|
26
|
+
FileUtils.rm_rf(release.target_path + name)
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
`#{options[:zip]} -v`
|
31
|
+
rescue Errno::ENOENT
|
32
|
+
raise RuntimeError, "Could not find zip in #{options[:zip].inspect}"
|
33
|
+
end
|
34
|
+
|
35
|
+
::Dir.chdir(release.build_path) do
|
36
|
+
`#{options[:zip]} -r -9 "#{release.target_path + name}" ./*`
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Roger::Release::Finalizers
|
2
|
+
class Base
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
@options = {}
|
6
|
+
@options.update(options) if options
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(release, options = {})
|
10
|
+
raise ArgumentError, "Implement in subclass"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require File.dirname(__FILE__) + "/finalizers/zip"
|
16
|
+
require File.dirname(__FILE__) + "/finalizers/dir"
|
17
|
+
require File.dirname(__FILE__) + "/finalizers/rsync"
|
18
|
+
require File.dirname(__FILE__) + "/finalizers/git_branch"
|
19
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
module Roger
|
3
|
+
|
4
|
+
# Inject VERSION / DATE (i.e. in TOC)
|
5
|
+
# r.inject({"VERSION" => release.version, "DATE" => release.date}, :into => %w{_doc/toc.html})
|
6
|
+
|
7
|
+
# Inject CHANGELOG
|
8
|
+
# r.inject({"CHANGELOG" => {:file => "", :filter => BlueCloth}}, :into => %w{_doc/changelog.html})
|
9
|
+
|
10
|
+
class Release::Injector
|
11
|
+
|
12
|
+
# @example Simple variable injection (replaces [VARIABLE] into all .css files)
|
13
|
+
# {"[VARIABLE]" => "replacement"}, :into => %w{**/*.css}
|
14
|
+
#
|
15
|
+
# @example Regex variable injection (replaces all matches into test.js files)
|
16
|
+
# {/\/\*\s*\[BANNER\]\s*\*\// => "replacement"}, :into => %w{javacripts/test.js}
|
17
|
+
#
|
18
|
+
# @example Simple variable injection with filtering (replaces [VARIABLE] with :content run through the markdown processor into all .html files)
|
19
|
+
# {"[VARIABLE]" => {:content => "# header one", :processor => "md"}, :into => %w{**/*.html}
|
20
|
+
#
|
21
|
+
# @example Full file injection (replaces all matches of [CHANGELOG] with the contents of "CHANGELOG.md" into _doc/changelog.html)
|
22
|
+
#
|
23
|
+
# {"CHANGELOG" => {:file => "CHANGELOG.md"}}, :into => %w{_doc/changelog.html}
|
24
|
+
#
|
25
|
+
# @example Full file injection with filtering (replaces all matches of [CHANGELOG] with the contents of "CHANGELOG" which ran through Markdown compresser into _doc/changelog.html)
|
26
|
+
#
|
27
|
+
# {"CHANGELOG" => {:file => "CHANGELOG", :processor => "md"}}, :into => %w{_doc/changelog.html}
|
28
|
+
#
|
29
|
+
# Processors are based on Tilt (https://github.com/rtomayko/tilt).
|
30
|
+
# Currently supported/tested processors are:
|
31
|
+
#
|
32
|
+
# * 'md' for Markdown (bluecloth)
|
33
|
+
#
|
34
|
+
# Injection files are relative to the :source_path
|
35
|
+
#
|
36
|
+
# @param [Hash] variables Variables to inject. See example for more info
|
37
|
+
# @option options [Array] :into An array of file globs relative to the build_path
|
38
|
+
def initialize(variables, options)
|
39
|
+
@variables = variables
|
40
|
+
@options = options
|
41
|
+
end
|
42
|
+
|
43
|
+
def call(release, options = {})
|
44
|
+
@options.update(options)
|
45
|
+
files = release.get_files(@options[:into])
|
46
|
+
|
47
|
+
files.each do |f|
|
48
|
+
c = File.read(f)
|
49
|
+
injected_vars = []
|
50
|
+
@variables.each do |variable, injection|
|
51
|
+
if c.gsub!(variable, get_content(injection, release))
|
52
|
+
injected_vars << variable
|
53
|
+
end
|
54
|
+
end
|
55
|
+
release.log(self, "Injected variables #{injected_vars.inspect} into #{f}") if injected_vars.size > 0
|
56
|
+
File.open(f,"w") { |fh| fh.write c }
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_content(injection, release)
|
62
|
+
case injection
|
63
|
+
when String
|
64
|
+
injection
|
65
|
+
when Hash
|
66
|
+
get_complex_injection(injection, release)
|
67
|
+
else
|
68
|
+
if injection.respond_to?(:to_s)
|
69
|
+
injection.to_s
|
70
|
+
else
|
71
|
+
raise ArgumentError, "Woah, what's this? #{injection.inspect}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_complex_injection(injection, release)
|
77
|
+
|
78
|
+
if injection[:file]
|
79
|
+
content = File.read(release.source_path + injection[:file])
|
80
|
+
else
|
81
|
+
content = injection[:content]
|
82
|
+
end
|
83
|
+
|
84
|
+
raise ArgumentError, "No :content or :file specified" if !content
|
85
|
+
|
86
|
+
if injection[:processor]
|
87
|
+
if tmpl = Tilt[injection[:processor]]
|
88
|
+
(tmpl.new{ content }).render
|
89
|
+
else
|
90
|
+
raise ArgumentError, "Unknown processor #{injection[:processor]}"
|
91
|
+
end
|
92
|
+
else
|
93
|
+
content
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Roger::Release::Processors
|
2
|
+
class Mockup < Base
|
3
|
+
|
4
|
+
attr_accessor :project
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@options = {
|
8
|
+
:env => {},
|
9
|
+
:match => ["**/*.{html,md,html.erb}"],
|
10
|
+
:skip => [/\Astylesheets/, /\Ajavascripts/]
|
11
|
+
}
|
12
|
+
|
13
|
+
@options.update(options) if options
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(release, options={})
|
17
|
+
self.project = release.project
|
18
|
+
|
19
|
+
options = {}.update(@options).update(options)
|
20
|
+
|
21
|
+
options[:env].update("MOCKUP_PROJECT" => project)
|
22
|
+
|
23
|
+
release.log(self, "Processing mockup files")
|
24
|
+
|
25
|
+
release.log(self, " Matching: #{options[:match].inspect}", true)
|
26
|
+
release.log(self, " Skiping : #{options[:skip].inspect}", true)
|
27
|
+
release.log(self, " Env : #{options[:env].inspect}", true)
|
28
|
+
release.log(self, " Files :", true)
|
29
|
+
|
30
|
+
release.get_files(options[:match], options[:skip]).each do |file_path|
|
31
|
+
release.log(self, " Extract: #{file_path}", true)
|
32
|
+
self.run_on_file!(file_path, options[:env])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def run_on_file!(file_path, env = {})
|
38
|
+
template = Roger::Template.open(file_path, :partials_path => self.project.partial_path, :layouts_path => self.project.layouts_path)
|
39
|
+
|
40
|
+
# Clean up source file
|
41
|
+
FileUtils.rm(file_path)
|
42
|
+
|
43
|
+
# Write out new file
|
44
|
+
File.open(self.target_path(file_path, template),"w"){|f| f.write(template.render(env.dup)) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Runs the extractor on a single file and return processed source.
|
48
|
+
def extract_source_from_file(file_path, env = {})
|
49
|
+
Roger::Template.open(file_path, :partials_path => self.project.partial_path, :layouts_path => self.project.layouts_path).render(env.dup)
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def target_path(path, template)
|
55
|
+
# 1. If we have a double extension we rip of the template it's own extension and be done with it
|
56
|
+
parts = File.basename(path.to_s).split(".")
|
57
|
+
dir = Pathname.new(File.dirname(path.to_s))
|
58
|
+
|
59
|
+
# 2. Try to figure out the extension based on the template's mime-type
|
60
|
+
mime_types = {
|
61
|
+
"text/html" => "html",
|
62
|
+
"text/css" => "css",
|
63
|
+
"application/javascript" => "js",
|
64
|
+
"text/xml" => "xml",
|
65
|
+
"application/xml" => "xml",
|
66
|
+
"text/csv" => "csv",
|
67
|
+
"application/json" => "json"
|
68
|
+
}
|
69
|
+
extension = mime_types[template.template.class.default_mime_type]
|
70
|
+
|
71
|
+
# Always return .html directly as it will cause too much trouble otherwise
|
72
|
+
if parts.last == "html"
|
73
|
+
return path
|
74
|
+
end
|
75
|
+
|
76
|
+
if parts.size > 2
|
77
|
+
# Strip extension
|
78
|
+
dir + parts[0..-2].join(".")
|
79
|
+
else
|
80
|
+
return path if extension.nil?
|
81
|
+
|
82
|
+
if parts.size > 1
|
83
|
+
# Strip extension and replace with extension
|
84
|
+
dir + (parts[0..-2] << extension).join(".")
|
85
|
+
else
|
86
|
+
# Let's just add the extension
|
87
|
+
dir + (parts << extension).join(".")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.dirname(__FILE__) + '../../../resolver'
|
2
|
+
|
3
|
+
module Roger::Release::Processors
|
4
|
+
class UrlRelativizer < Base
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@options = {
|
8
|
+
:url_attributes => %w{src href action},
|
9
|
+
:match => ["**/*.html"],
|
10
|
+
:skip => []
|
11
|
+
}
|
12
|
+
|
13
|
+
@options.update(options) if options
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(release, options={})
|
17
|
+
options = {}.update(@options).update(options)
|
18
|
+
|
19
|
+
release.log(self, "Relativizing all URLS in #{options[:match].inspect} files in attributes #{options[:url_attributes].inspect}, skipping #{options[:skip].any? ? options[:skip].inspect : "none" }")
|
20
|
+
|
21
|
+
@resolver = Roger::Resolver.new(release.build_path)
|
22
|
+
release.get_files(options[:match], options[:skip]).each do |file_path|
|
23
|
+
release.debug(self, "Relativizing URLS in #{file_path}") do
|
24
|
+
orig_source = File.read(file_path)
|
25
|
+
File.open(file_path,"w") do |f|
|
26
|
+
doc = Hpricot(orig_source)
|
27
|
+
options[:url_attributes].each do |attribute|
|
28
|
+
(doc/"*[@#{attribute}]").each do |tag|
|
29
|
+
converted_url = @resolver.url_to_relative_url(tag[attribute], file_path)
|
30
|
+
release.debug(self, "Converting '#{tag[attribute]}' to '#{converted_url}'")
|
31
|
+
case converted_url
|
32
|
+
when String
|
33
|
+
tag[attribute] = converted_url
|
34
|
+
when nil
|
35
|
+
release.log(self, "Could not resolve link #{tag[attribute]} in #{file_path}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
f.write(doc.to_original_html)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Roger::Release::Processors
|
2
|
+
class Base
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
@options = {}
|
6
|
+
@options.update(options) if options
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def call(release, options = {})
|
11
|
+
raise ArgumentError, "Implement in subclass"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require File.dirname(__FILE__) + "/processors/mockup"
|
17
|
+
require File.dirname(__FILE__) + "/processors/url_relativizer"
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Roger::Release::Scm
|
4
|
+
class Git < Base
|
5
|
+
|
6
|
+
# @option config [String] :ref Ref to use for current tag
|
7
|
+
# @option config [String, Pathname] :path Path to working dir
|
8
|
+
def initialize(config={})
|
9
|
+
super(config)
|
10
|
+
@config[:ref] ||= "HEAD"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Version is either:
|
14
|
+
# - the tagged version number (first "v" will be stripped) or
|
15
|
+
# - the return value of "git describe --tags HEAD"
|
16
|
+
# - the short SHA1 if there hasn't been a previous tag
|
17
|
+
def version
|
18
|
+
get_scm_data if @_version.nil?
|
19
|
+
@_version
|
20
|
+
end
|
21
|
+
|
22
|
+
# Date will be Time.now if it can't be determined from GIT repository
|
23
|
+
def date
|
24
|
+
get_scm_data if @_date.nil?
|
25
|
+
@_date
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous
|
29
|
+
self.class.new(@config.dup.update(:ref => get_previous_tag_name))
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def get_previous_tag_name
|
35
|
+
# Get list of SHA1 that have a ref
|
36
|
+
begin
|
37
|
+
sha1s = `git --git-dir=#{git_dir} log --pretty='%H' --simplify-by-decoration`.split("\n")
|
38
|
+
tags = []
|
39
|
+
while tags.size < 2 && sha1s.any?
|
40
|
+
sha1 = sha1s.shift
|
41
|
+
tag = `git --git-dir=#{git_dir} describe --tags --exact-match #{sha1} 2>/dev/null`.strip
|
42
|
+
tags << tag if !tag.empty?
|
43
|
+
end
|
44
|
+
tags.last
|
45
|
+
rescue
|
46
|
+
raise "Could not get previous tag"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def git_dir
|
51
|
+
@git_dir ||= find_git_dir(@config[:path])
|
52
|
+
end
|
53
|
+
|
54
|
+
# Some hackery to determine if we're on a tagged version or not
|
55
|
+
def get_scm_data(ref = @config[:ref])
|
56
|
+
@_version = ""
|
57
|
+
@_date = Time.now
|
58
|
+
begin
|
59
|
+
if File.exist?(git_dir)
|
60
|
+
@_version = `git --git-dir=#{git_dir} describe --tags #{ref} 2>&1`
|
61
|
+
|
62
|
+
if $?.to_i > 0
|
63
|
+
# HEAD is not a tagged verison, get the short SHA1 instead
|
64
|
+
@_version = `git --git-dir=#{git_dir} show #{ref} --format=format:"%h" -s 2>&1`
|
65
|
+
else
|
66
|
+
# HEAD is a tagged version, if version is prefixed with "v" it will be stripped off
|
67
|
+
@_version.gsub!(/^v/,"")
|
68
|
+
end
|
69
|
+
@_version.strip!
|
70
|
+
|
71
|
+
# Get the date in epoch time
|
72
|
+
date = `git --git-dir=#{git_dir} show #{ref} --format=format:"%ct" -s 2>&1`
|
73
|
+
if date =~ /\d+/
|
74
|
+
@_date = Time.at(date.to_i)
|
75
|
+
else
|
76
|
+
@_date = Time.now
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
rescue RuntimeError => e
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# Find the git dir
|
86
|
+
def find_git_dir(path)
|
87
|
+
path = Pathname.new(path).realpath
|
88
|
+
while path.parent != path && !(path + ".git").directory?
|
89
|
+
path = path.parent
|
90
|
+
end
|
91
|
+
|
92
|
+
path = path + ".git"
|
93
|
+
|
94
|
+
raise "Could not find suitable .git dir in #{path}" if !path.directory?
|
95
|
+
|
96
|
+
path
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Roger::Release::Scm
|
2
|
+
class Base
|
3
|
+
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(config={})
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the release version string from the SCM
|
11
|
+
#
|
12
|
+
# @return String The current version string
|
13
|
+
def version
|
14
|
+
raise "Implement in subclass"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the release version date from the SCM
|
18
|
+
def date
|
19
|
+
raise "Implement in subclass"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a Release::Scm object with the previous version's data
|
23
|
+
#
|
24
|
+
# @return Roger::Release::Scm The previous version
|
25
|
+
def previous
|
26
|
+
raise "Implement in subclass"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
require File.dirname(__FILE__) + "/scm/git"
|