mamiya 0.0.1.alpha2
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/bin/mamiya +17 -0
- data/config.example.yml +11 -0
- data/docs/sequences/deploy.png +0 -0
- data/docs/sequences/deploy.uml +58 -0
- data/example.rb +74 -0
- data/lib/mamiya.rb +5 -0
- data/lib/mamiya/agent.rb +181 -0
- data/lib/mamiya/agent/actions.rb +12 -0
- data/lib/mamiya/agent/fetcher.rb +137 -0
- data/lib/mamiya/agent/handlers/abstract.rb +20 -0
- data/lib/mamiya/agent/handlers/fetch.rb +68 -0
- data/lib/mamiya/cli.rb +322 -0
- data/lib/mamiya/cli/client.rb +172 -0
- data/lib/mamiya/config.rb +57 -0
- data/lib/mamiya/dsl.rb +192 -0
- data/lib/mamiya/helpers/git.rb +75 -0
- data/lib/mamiya/logger.rb +190 -0
- data/lib/mamiya/master.rb +118 -0
- data/lib/mamiya/master/agent_monitor.rb +146 -0
- data/lib/mamiya/master/agent_monitor_handlers.rb +44 -0
- data/lib/mamiya/master/web.rb +148 -0
- data/lib/mamiya/package.rb +122 -0
- data/lib/mamiya/script.rb +117 -0
- data/lib/mamiya/steps/abstract.rb +19 -0
- data/lib/mamiya/steps/build.rb +72 -0
- data/lib/mamiya/steps/extract.rb +26 -0
- data/lib/mamiya/steps/fetch.rb +24 -0
- data/lib/mamiya/steps/push.rb +34 -0
- data/lib/mamiya/storages.rb +17 -0
- data/lib/mamiya/storages/abstract.rb +48 -0
- data/lib/mamiya/storages/mock.rb +61 -0
- data/lib/mamiya/storages/s3.rb +127 -0
- data/lib/mamiya/util/label_matcher.rb +38 -0
- data/lib/mamiya/version.rb +3 -0
- data/mamiya.gemspec +35 -0
- data/misc/logger_test.rb +12 -0
- data/spec/agent/actions_spec.rb +37 -0
- data/spec/agent/fetcher_spec.rb +199 -0
- data/spec/agent/handlers/fetch_spec.rb +121 -0
- data/spec/agent_spec.rb +255 -0
- data/spec/config_spec.rb +50 -0
- data/spec/dsl_spec.rb +291 -0
- data/spec/fixtures/dsl_test_load.rb +1 -0
- data/spec/fixtures/dsl_test_use.rb +1 -0
- data/spec/fixtures/helpers/foo.rb +1 -0
- data/spec/fixtures/test-package-source/.mamiya.meta.json +1 -0
- data/spec/fixtures/test-package-source/greeting +1 -0
- data/spec/fixtures/test-package.tar.gz +0 -0
- data/spec/fixtures/test.yml +4 -0
- data/spec/logger_spec.rb +68 -0
- data/spec/master/agent_monitor_spec.rb +269 -0
- data/spec/master/web_spec.rb +121 -0
- data/spec/master_spec.rb +94 -0
- data/spec/package_spec.rb +394 -0
- data/spec/script_spec.rb +78 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/steps/build_spec.rb +261 -0
- data/spec/steps/extract_spec.rb +68 -0
- data/spec/steps/fetch_spec.rb +96 -0
- data/spec/steps/push_spec.rb +73 -0
- data/spec/storages/abstract_spec.rb +22 -0
- data/spec/storages/s3_spec.rb +342 -0
- data/spec/storages_spec.rb +33 -0
- data/spec/support/dummy_serf.rb +70 -0
- data/spec/util/label_matcher_spec.rb +85 -0
- metadata +272 -0
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'mamiya/cli'
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'uri'
|
6
|
+
require 'json'
|
7
|
+
require 'thor'
|
8
|
+
|
9
|
+
module Mamiya
|
10
|
+
class CLI < Thor
|
11
|
+
class Client < Thor
|
12
|
+
class_option :master, aliases: '-u', type: :string
|
13
|
+
class_option :application, aliases: %w(-a --app), type: :string
|
14
|
+
|
15
|
+
desc "list-applications", "list applications"
|
16
|
+
def list_applications
|
17
|
+
puts master_get('/packages')["applications"]
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "list-packages", "list-packages"
|
21
|
+
def list_packages
|
22
|
+
puts master_get("/packages/#{application}")["packages"]
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "show-package", "show package meta data"
|
26
|
+
method_option :format, aliases: %w(-f), type: :string, default: 'pp'
|
27
|
+
def show_package(package)
|
28
|
+
meta = master_get("/packages/#{application}/#{package}")
|
29
|
+
|
30
|
+
case options[:format]
|
31
|
+
when 'pp'
|
32
|
+
require 'pp'
|
33
|
+
pp meta
|
34
|
+
when 'json'
|
35
|
+
require 'json'
|
36
|
+
puts meta.to_json
|
37
|
+
when 'yaml'
|
38
|
+
require 'yaml'
|
39
|
+
puts meta.to_yaml
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "list-agents", 'list agents'
|
44
|
+
def list_agents
|
45
|
+
payload = master_get("/agents")
|
46
|
+
|
47
|
+
agents = payload["agents"].keys
|
48
|
+
|
49
|
+
agents.each do |agent|
|
50
|
+
puts "#{agent}\talive"
|
51
|
+
end
|
52
|
+
payload["failed_agents"].each do |agent|
|
53
|
+
puts "#{agent}\tfailed"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "show-agent AGENT", 'Show agent'
|
58
|
+
method_option :format, aliases: %w(-f), type: :string, default: 'pp'
|
59
|
+
def show_agent(agent)
|
60
|
+
agent = master_get("/agents/#{agent}")
|
61
|
+
|
62
|
+
case options[:format]
|
63
|
+
when 'pp'
|
64
|
+
require 'pp'
|
65
|
+
pp agent
|
66
|
+
when 'json'
|
67
|
+
require 'json'
|
68
|
+
puts agent.to_json
|
69
|
+
when 'yaml'
|
70
|
+
require 'yaml'
|
71
|
+
puts agent.to_yaml
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "show-distribution package", "Show package distribution status"
|
76
|
+
method_option :format, aliases: %w(-f), type: :string, default: 'text'
|
77
|
+
method_option :verbose, aliases: %w(-v), type: :boolean
|
78
|
+
def show_distribution(package)
|
79
|
+
dist = master_get("/packages/#{application}/#{package}/distribution")
|
80
|
+
|
81
|
+
case options[:format]
|
82
|
+
when 'json'
|
83
|
+
require 'json'
|
84
|
+
puts dist.to_json
|
85
|
+
return
|
86
|
+
|
87
|
+
when 'yaml'
|
88
|
+
require 'yaml'
|
89
|
+
puts dist.to_yaml
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
total = dist['distributed_count'] + dist['not_distributed_count']
|
94
|
+
puts <<-EOF
|
95
|
+
Distribution status of #{application}/#{package}
|
96
|
+
|
97
|
+
ratio (distributed:total-agents): #{"%.1f" % ((dist['distributed_count']/total.to_f)*100)}%
|
98
|
+
number of agents have package: #{dist['distributed_count']}
|
99
|
+
number of agents don't have package: #{dist['not_distributed_count']}
|
100
|
+
EOF
|
101
|
+
|
102
|
+
if options[:verbose]
|
103
|
+
puts ""
|
104
|
+
dist['not_distributed'].each do |name|
|
105
|
+
puts "#{name}\tnot_distributed"
|
106
|
+
end
|
107
|
+
dist['distributed'].each do |name|
|
108
|
+
puts "#{name}\tdistributed"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
desc "distribute package", "order distributing package to agents"
|
114
|
+
def distribute(package)
|
115
|
+
p master_post("/packages/#{application}/#{package}/distribute")
|
116
|
+
end
|
117
|
+
|
118
|
+
desc "refresh", "order refreshing agent status"
|
119
|
+
def refresh
|
120
|
+
p master_post('/agents/refresh')
|
121
|
+
end
|
122
|
+
|
123
|
+
desc "deploy PACKAGE", "Run distribute->prepare->finalize"
|
124
|
+
def deploy
|
125
|
+
end
|
126
|
+
|
127
|
+
desc "rollback", "Switch back to previous release then finalize"
|
128
|
+
def rollback
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def fatal!(msg)
|
134
|
+
$stderr.puts msg
|
135
|
+
exit 1
|
136
|
+
end
|
137
|
+
|
138
|
+
def application
|
139
|
+
options[:application] or fatal!('specify application')
|
140
|
+
end
|
141
|
+
|
142
|
+
def master_get(path)
|
143
|
+
master_http.start do |http|
|
144
|
+
JSON.parse http.get(path).tap(&:value).body
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def master_post(path, data='')
|
149
|
+
master_http.start do |http|
|
150
|
+
response = http.post(path, data).tap(&:value)
|
151
|
+
response.code == '204' ? true : JSON.parse(response.tap(&:value).body)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def master_http
|
156
|
+
url = master_url
|
157
|
+
Net::HTTP.new(url.host, url.port).tap do |http|
|
158
|
+
http.use_ssl = true if url.scheme == 'https'
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def master_url
|
163
|
+
url = ENV["MAMIYA_MASTER_URL"] || options[:master]
|
164
|
+
fatal! 'specify master URL via --master(-u) option or $MAMIYA_MASTER_URL' unless url
|
165
|
+
URI.parse(url)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
desc "client", "client for master"
|
170
|
+
subcommand "client", Client
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'mamiya/storages'
|
3
|
+
|
4
|
+
module Mamiya
|
5
|
+
class Config
|
6
|
+
def self.load(file)
|
7
|
+
self.new YAML.load_file(file)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(config_hash = {})
|
11
|
+
@config = symbolize_keys_in(config_hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
@config[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
@config[key] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def storage_class
|
23
|
+
self[:storage] && Storages.find(self[:storage][:type])
|
24
|
+
end
|
25
|
+
|
26
|
+
def deploy_to_for_app(app)
|
27
|
+
# TODO: test
|
28
|
+
app = app.to_sym
|
29
|
+
|
30
|
+
if self[:apps] && self[:applications][app]
|
31
|
+
Pathname.new(self[:applications][app][:deploy_to])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def releases_path_for_app(app)
|
36
|
+
# TODO: test
|
37
|
+
deploy_to_for_app(app).join('releases')
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def symbolize_keys_in(hash)
|
43
|
+
Hash[hash.map { |k, v|
|
44
|
+
case v
|
45
|
+
when Hash
|
46
|
+
v = symbolize_keys_in(v)
|
47
|
+
when Array
|
48
|
+
if v.find { |_| _.kind_of?(Hash) }
|
49
|
+
v = v.map { |_| _.kind_of?(Hash) ? symbolize_keys_in(_) : _ }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
[k.to_sym, v]
|
54
|
+
}]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/mamiya/dsl.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'mamiya/util/label_matcher'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Mamiya
|
5
|
+
class DSL
|
6
|
+
class TaskNotDefinedError < Exception; end
|
7
|
+
class HelperNotFound < Exception; end
|
8
|
+
|
9
|
+
##
|
10
|
+
# Creates new DSL environment.
|
11
|
+
def initialize
|
12
|
+
@variables = {}
|
13
|
+
@tasks = {}
|
14
|
+
@hooks = {}
|
15
|
+
@eval_lock = Mutex.new
|
16
|
+
@use_lock = Mutex.new
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :hooks, :tasks
|
20
|
+
|
21
|
+
##
|
22
|
+
# Returns Hash of default setting variables.
|
23
|
+
def self.defaults
|
24
|
+
@defaults ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.define_variable_accessor(name) # :nodoc:
|
28
|
+
k = name.to_sym
|
29
|
+
return if self.instance_methods.include?(k)
|
30
|
+
|
31
|
+
define_method(k) { @variables[k] || self.class.defaults[k] }
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Sets default value +value+ for variable name +key+.
|
36
|
+
# Values set by this method will available for all instances of same class.
|
37
|
+
def self.set_default(key, value)
|
38
|
+
k = key.to_sym
|
39
|
+
defaults[k] = value
|
40
|
+
self.define_variable_accessor(k)
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Add hook point with name +name+.
|
45
|
+
# This defines method with same name in class to call and define hooks.
|
46
|
+
def self.add_hook(name, attributes={})
|
47
|
+
define_method(name) do |*args, &block|
|
48
|
+
@hooks[name] ||= []
|
49
|
+
|
50
|
+
if block
|
51
|
+
hook_name = args.shift if args.first.kind_of?(String)
|
52
|
+
options = args.pop if args.last.kind_of?(Hash)
|
53
|
+
|
54
|
+
hook = {block: block, options: options || {}, name: hook_name}
|
55
|
+
case args.first
|
56
|
+
when :overwrite
|
57
|
+
@hooks[name] = [hook]
|
58
|
+
when :prepend
|
59
|
+
@hooks[name][0,0] = [hook]
|
60
|
+
else
|
61
|
+
@hooks[name] << hook
|
62
|
+
end
|
63
|
+
|
64
|
+
else
|
65
|
+
matcher = Mamiya::Util::LabelMatcher::Simple.new(args)
|
66
|
+
Proc.new { |*args|
|
67
|
+
filtered_hooks = @hooks[name].reject { |hook|
|
68
|
+
options = hook[:options]
|
69
|
+
|
70
|
+
(options[:only] && !matcher.match?(*options[:only] )) ||
|
71
|
+
(options[:except] && matcher.match?(*options[:except]))
|
72
|
+
}
|
73
|
+
|
74
|
+
if attributes[:chain]
|
75
|
+
init = args.shift
|
76
|
+
filtered_hooks.inject(init) do |result, hook|
|
77
|
+
hook[:block].call(result, *args)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
filtered_hooks.each do |hook|
|
81
|
+
hook[:block].call *args
|
82
|
+
end
|
83
|
+
end
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# :call-seq:
|
91
|
+
# evaluate!(string [, filename [, lineno]])
|
92
|
+
# evaluate! { block }
|
93
|
+
#
|
94
|
+
# Evaluates given string or block in DSL environment.
|
95
|
+
def evaluate!(str = nil, filename = nil, lineno = nil, &block)
|
96
|
+
@eval_lock.synchronize {
|
97
|
+
begin
|
98
|
+
if block_given?
|
99
|
+
self.instance_eval(&block)
|
100
|
+
elsif str
|
101
|
+
@file = filename if filename
|
102
|
+
|
103
|
+
if str && filename && lineno
|
104
|
+
self.instance_eval(str, filename, lineno)
|
105
|
+
elsif str && filename
|
106
|
+
self.instance_eval(str, filename)
|
107
|
+
elsif str
|
108
|
+
self.instance_eval(str)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
ensure
|
112
|
+
@file = nil
|
113
|
+
end
|
114
|
+
}
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Evaluates specified file +file+ in DSL environment.
|
120
|
+
def load!(file)
|
121
|
+
evaluate! File.read(file), file, 1
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# (DSL) Find file using +name+ from current +load_path+ then load.
|
126
|
+
# +options+ will be available as variable +options+ in loaded file.
|
127
|
+
def use(name, options={})
|
128
|
+
helper_file = find_helper_file(name)
|
129
|
+
raise HelperNotFound unless helper_file
|
130
|
+
|
131
|
+
@use_lock.lock unless @use_lock.owned? # to avoid lock recursively
|
132
|
+
|
133
|
+
@_options = options
|
134
|
+
self.instance_eval File.read(helper_file).prepend("options = @_options; @_options = nil;\n"), helper_file, 1
|
135
|
+
|
136
|
+
ensure
|
137
|
+
@_options = nil
|
138
|
+
@use_lock.unlock if @use_lock.owned?
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# (DSL) Set value +value+ for variable named +key+.
|
143
|
+
def set(key, value)
|
144
|
+
k = key.to_sym
|
145
|
+
self.class.define_variable_accessor(key) unless self.methods.include?(k)
|
146
|
+
@variables[k] = value
|
147
|
+
end
|
148
|
+
|
149
|
+
##
|
150
|
+
# (DSL) Set value +value+ for variable named +key+ unless value is present for the variable.
|
151
|
+
def set_default(key, value)
|
152
|
+
k = key.to_sym
|
153
|
+
return @variables[k] if @variables.key?(k)
|
154
|
+
set(k, value)
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# (DSL) Define task named +name+ with given block.
|
159
|
+
def task(name, &block)
|
160
|
+
@tasks[name] = block
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# (DSL) Invoke task named +name+.
|
165
|
+
def invoke(name)
|
166
|
+
raise TaskNotDefinedError unless @tasks[name]
|
167
|
+
self.instance_eval &@tasks[name]
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Returns current load path used by +use+ method.
|
172
|
+
def load_path
|
173
|
+
(@variables[:load_path] ||= []) +
|
174
|
+
[
|
175
|
+
"#{__dir__}/helpers",
|
176
|
+
*(@file ? ["#{File.dirname(@file)}/helpers"] : [])
|
177
|
+
]
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def find_helper_file(name) # :nodoc:
|
183
|
+
load_path.find do |_| # Using find to return nil when not found
|
184
|
+
path = File.join(_, "#{name}.rb")
|
185
|
+
break path if File.exists?(path)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# TODO: hook call context methods
|
190
|
+
#https://gist.github.com/sorah/9263951
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
set_default :git_remote, 'origin'
|
4
|
+
set_default :commit, "#{self.git_remote}/HEAD"
|
5
|
+
|
6
|
+
def git_ignored_files
|
7
|
+
git_clean_out = `git clean -ndx`.lines
|
8
|
+
prefix = /^Would (?:remove|skip repository) /
|
9
|
+
|
10
|
+
if git_clean_out.any? { |_| prefix !~ _ }
|
11
|
+
puts git_clean_out
|
12
|
+
raise "`git clean -ndx` doesn't return line starting with 'Would remove' or 'Would skip'"
|
13
|
+
end
|
14
|
+
|
15
|
+
excludes = git_clean_out.grep(prefix).map{ |_| _.sub(prefix, '').chomp }
|
16
|
+
if package_under
|
17
|
+
excludes.grep(/^#{Regexp.escape(package_under)}/).map{ |_| _.sub(/^#{Regexp.escape(package_under)}\/?/, '') }
|
18
|
+
else
|
19
|
+
excludes
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def git_head
|
24
|
+
git_show = `git show --pretty=fuller -s`
|
25
|
+
commit, comment = git_show.split(/\n\n/, 2)
|
26
|
+
|
27
|
+
{
|
28
|
+
commit: commit.match(/^commit (.+)$/)[1],
|
29
|
+
author: commit.match(/^Author:\s*(?<name>.+?) <(?<email>.+?)>$/).
|
30
|
+
tap {|match| break Hash[match.names.map {|name| [name.to_sym, match[name]] }] },
|
31
|
+
author_date: Time.parse(commit.match(/^AuthorDate:\s*(.+)$/)[1]),
|
32
|
+
committer: commit.match(/^Commit:\s*(?<name>.+?) <(?<email>.+?)>$/).
|
33
|
+
tap {|match| break Hash[match.names.map {|name| [name.to_sym, match[name]] }] },
|
34
|
+
commit_date: Time.parse(commit.match(/^CommitDate:\s*(.+)$/)[1]),
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
prepare_build do |update|
|
39
|
+
logger = self.logger['git']
|
40
|
+
|
41
|
+
if !update && !self.repository
|
42
|
+
logger.warn 'Skipping cloning repository because script.repository not set'
|
43
|
+
elsif !update
|
44
|
+
run "git", "clone", self.repository, self.build_from
|
45
|
+
end
|
46
|
+
|
47
|
+
Dir.chdir(self.build_from) do
|
48
|
+
logger.info Dir.pwd
|
49
|
+
run "git", "fetch", self.git_remote
|
50
|
+
run "git", "remote", "prune", self.git_remote, allow_failure: true
|
51
|
+
run "git", "fetch", "--tags", self.git_remote
|
52
|
+
|
53
|
+
run "git", "reset", "--hard", self.commit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
if options[:exclude_git_clean_targets]
|
58
|
+
build(:prepend) do
|
59
|
+
set :exclude_from_package, exclude_from_package + git_ignored_files()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
options[:add_commit_hash_to_package_name] = true unless options.key?(:add_commit_hash_to_package_name)
|
64
|
+
if options[:add_commit_hash_to_package_name]
|
65
|
+
package_name do |candidate|
|
66
|
+
candidate + [git_head[:commit]]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
options[:include_head_commit_to_meta] = true unless options.key?(:include_head_commit_to_meta)
|
71
|
+
if options[:include_head_commit_to_meta]
|
72
|
+
package_meta do |candidate|
|
73
|
+
candidate.merge(git: git_head())
|
74
|
+
end
|
75
|
+
end
|