luban 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +37 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/luban +3 -0
- data/lib/luban/deployment/cli/application/authenticator.rb +106 -0
- data/lib/luban/deployment/cli/application/base.rb +179 -0
- data/lib/luban/deployment/cli/application/builder.rb +67 -0
- data/lib/luban/deployment/cli/application/publisher.rb +215 -0
- data/lib/luban/deployment/cli/application/repository.rb +175 -0
- data/lib/luban/deployment/cli/application/scm/git.rb +49 -0
- data/lib/luban/deployment/cli/application/scm/rsync.rb +47 -0
- data/lib/luban/deployment/cli/application.rb +5 -0
- data/lib/luban/deployment/cli/command.rb +360 -0
- data/lib/luban/deployment/cli/package/binary.rb +241 -0
- data/lib/luban/deployment/cli/package/dependency.rb +49 -0
- data/lib/luban/deployment/cli/package/dependency_set.rb +71 -0
- data/lib/luban/deployment/cli/package/installer/core.rb +98 -0
- data/lib/luban/deployment/cli/package/installer/install.rb +330 -0
- data/lib/luban/deployment/cli/package/installer/paths.rb +81 -0
- data/lib/luban/deployment/cli/package/installer.rb +3 -0
- data/lib/luban/deployment/cli/package/service.rb +17 -0
- data/lib/luban/deployment/cli/package/worker.rb +43 -0
- data/lib/luban/deployment/cli/package.rb +6 -0
- data/lib/luban/deployment/cli/project.rb +94 -0
- data/lib/luban/deployment/cli.rb +4 -0
- data/lib/luban/deployment/configuration/core.rb +67 -0
- data/lib/luban/deployment/configuration/filter.rb +54 -0
- data/lib/luban/deployment/configuration/question.rb +38 -0
- data/lib/luban/deployment/configuration/server.rb +70 -0
- data/lib/luban/deployment/configuration/server_set.rb +86 -0
- data/lib/luban/deployment/configuration.rb +5 -0
- data/lib/luban/deployment/error.rb +5 -0
- data/lib/luban/deployment/helpers/configuration.rb +159 -0
- data/lib/luban/deployment/helpers/utils.rb +180 -0
- data/lib/luban/deployment/helpers.rb +2 -0
- data/lib/luban/deployment/packages/bundler.rb +81 -0
- data/lib/luban/deployment/packages/git.rb +37 -0
- data/lib/luban/deployment/packages/openssl.rb +59 -0
- data/lib/luban/deployment/packages/ruby.rb +125 -0
- data/lib/luban/deployment/packages/rubygems.rb +89 -0
- data/lib/luban/deployment/packages/yaml.rb +33 -0
- data/lib/luban/deployment/parameters.rb +160 -0
- data/lib/luban/deployment/runner.rb +99 -0
- data/lib/luban/deployment/templates/envrc.erb +30 -0
- data/lib/luban/deployment/templates/unset_envrc.erb +28 -0
- data/lib/luban/deployment/version.rb +5 -0
- data/lib/luban/deployment/worker/base.rb +71 -0
- data/lib/luban/deployment/worker/controller.rb +11 -0
- data/lib/luban/deployment/worker/local.rb +19 -0
- data/lib/luban/deployment/worker/remote.rb +55 -0
- data/lib/luban/deployment/worker/task.rb +25 -0
- data/lib/luban/deployment/worker.rb +4 -0
- data/lib/luban/deployment.rb +8 -0
- data/lib/luban.rb +4 -0
- data/luban.gemspec +29 -0
- metadata +174 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
module Package
|
4
|
+
class Binary < Luban::Deployment::Command
|
5
|
+
using Luban::CLI::CoreRefinements
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def inherited(subclass)
|
9
|
+
super
|
10
|
+
# Ensure package dependencies from base class
|
11
|
+
# got inherited to its subclasses
|
12
|
+
|
13
|
+
subclass.instance_variable_set(
|
14
|
+
'@dependencies',
|
15
|
+
Marshal.load(Marshal.dump(dependencies))
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def dependencies
|
20
|
+
@dependencies ||= DependencySet.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def apply_to(version, &blk)
|
24
|
+
dependencies.apply_to(version, &blk)
|
25
|
+
end
|
26
|
+
|
27
|
+
def required_packages_for(version)
|
28
|
+
dependencies.dependencies_for(version)
|
29
|
+
end
|
30
|
+
|
31
|
+
def package_class(package)
|
32
|
+
require_path = package_require_path(package)
|
33
|
+
require require_path
|
34
|
+
package_base_class(require_path)
|
35
|
+
rescue LoadError => e
|
36
|
+
abort "Aborted! Failed to load package #{require_path}: #{e.message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def worker_class(worker, package: self)
|
40
|
+
if package.is_a?(Class)
|
41
|
+
if package == self
|
42
|
+
super(worker)
|
43
|
+
else
|
44
|
+
package.worker_class(worker)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
package_class(package).worker_class(worker)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def package_require_path(package_name)
|
54
|
+
package_require_root.join(package_name.to_s.gsub(':', '/'))
|
55
|
+
end
|
56
|
+
|
57
|
+
def package_require_root
|
58
|
+
@default_package_root ||= Pathname.new("luban/deployment/packages")
|
59
|
+
end
|
60
|
+
|
61
|
+
def package_base_class(path)
|
62
|
+
Object.const_get(path.to_s.camelcase, false)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
include Luban::Deployment::Parameters::Project
|
67
|
+
include Luban::Deployment::Parameters::Application
|
68
|
+
include Luban::Deployment::Command::Tasks::Install
|
69
|
+
|
70
|
+
attr_reader :current_version
|
71
|
+
|
72
|
+
def package_options; @package_options ||= {}; end
|
73
|
+
|
74
|
+
def update_package_options(version, **opts)
|
75
|
+
unless package_options.has_key?(version)
|
76
|
+
package_options[version] =
|
77
|
+
{ name: name.to_s }.merge!(decompose_version(version))
|
78
|
+
end
|
79
|
+
@current_version = version if opts[:current]
|
80
|
+
package_options[version].merge!(opts)
|
81
|
+
end
|
82
|
+
|
83
|
+
def has_version?(version)
|
84
|
+
package_options.has_key?(version)
|
85
|
+
end
|
86
|
+
|
87
|
+
def decompose_version(version)
|
88
|
+
{ major_version: version, patch_level: '' }
|
89
|
+
end
|
90
|
+
|
91
|
+
def versions; package_options.keys; end
|
92
|
+
|
93
|
+
%i(install uninstall cleanup_all update_binstubs
|
94
|
+
get_summary which_current whence_origin).each do |m|
|
95
|
+
define_task_method(m, worker: :installer)
|
96
|
+
end
|
97
|
+
|
98
|
+
def install_all(args:, opts:)
|
99
|
+
versions.each do |v|
|
100
|
+
install(args: args, opts: opts.merge(version: v))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def uninstall_all(args:, opts:)
|
105
|
+
versions.each do |v|
|
106
|
+
uninstall(args: args, opts: opts.merge(version: v))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :uninstall!, :uninstall
|
111
|
+
def uninstall(args:, opts:)
|
112
|
+
servers = select_servers(opts[:roles], opts[:hosts])
|
113
|
+
apps = parent.other_package_users_for(name, opts[:version], servers: servers)
|
114
|
+
if apps.empty? or opts[:force]
|
115
|
+
uninstall!(args: args, opts: opts)
|
116
|
+
else
|
117
|
+
puts "Skipped. #{name}-#{opts[:version]} is being referenced by #{apps.join(', ')}. " +
|
118
|
+
"use -f to force uninstalling if necessary."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def cleanup(args:, opts:)
|
123
|
+
versions.each do |v|
|
124
|
+
cleanup_all(args: args, opts: opts.merge(version: v))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def binstubs(args:, opts:)
|
129
|
+
if current_version
|
130
|
+
update_binstubs(args: args, opts: opts.merge(version: current_version))
|
131
|
+
else
|
132
|
+
versions.each do |v|
|
133
|
+
update_binstubs(args: args, opts: opts.merge(version: v))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def show_current(args:, opts:)
|
139
|
+
if current_version
|
140
|
+
print_summary(get_summary(args: args, opts: opts.merge(version: current_version)))
|
141
|
+
else
|
142
|
+
puts " Warning! No current version of #{display_name} is specified."
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def show_summary(args:, opts:)
|
147
|
+
versions.each do |v|
|
148
|
+
print_summary(get_summary(args: args, opts: opts.merge(version: v)))
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def which(args:, opts:)
|
153
|
+
if current_version
|
154
|
+
print_summary(which_current(args: args, opts: opts.merge(version: current_version)))
|
155
|
+
else
|
156
|
+
puts " Warning! No current version of #{display_name} is specified."
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def whence(args:, opts:)
|
161
|
+
versions.each do |v|
|
162
|
+
print_summary(whence_origin(args: args, opts: opts.merge(version: v)))
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
protected
|
167
|
+
|
168
|
+
def set_parameters
|
169
|
+
self.config = parent.config
|
170
|
+
end
|
171
|
+
|
172
|
+
def setup_tasks
|
173
|
+
super
|
174
|
+
setup_install_tasks
|
175
|
+
end
|
176
|
+
|
177
|
+
def compose_task_options(opts)
|
178
|
+
opts = super
|
179
|
+
version = opts[:version]
|
180
|
+
unless version.nil?
|
181
|
+
# Merge package options into task options without nil ones
|
182
|
+
opts = package_options[version].merge(opts.reject { |_, v| v.nil? })
|
183
|
+
end
|
184
|
+
opts
|
185
|
+
end
|
186
|
+
|
187
|
+
def setup_descriptions
|
188
|
+
desc "Manage package #{display_name}"
|
189
|
+
long_desc "Manage the deployment of package #{display_name} in #{parent.class.name}"
|
190
|
+
end
|
191
|
+
|
192
|
+
def setup_install_tasks
|
193
|
+
super
|
194
|
+
|
195
|
+
undef_task :build
|
196
|
+
undef_task :destroy
|
197
|
+
|
198
|
+
_package = self
|
199
|
+
task :install do
|
200
|
+
desc "Install a given version"
|
201
|
+
option :version, "Version to install", short: :v, required: true,
|
202
|
+
assure: ->(v){ _package.versions.include?(v) }
|
203
|
+
switch :force, "Force to install", short: :f
|
204
|
+
action! :install
|
205
|
+
end
|
206
|
+
|
207
|
+
task :install_all do
|
208
|
+
desc "Install all versions"
|
209
|
+
switch :force, "Force to install", short: :f
|
210
|
+
action! :install_all
|
211
|
+
end
|
212
|
+
|
213
|
+
task :uninstall do
|
214
|
+
desc "Uninstall a given version"
|
215
|
+
option :version, "Version to uninstall", short: :v, required: true,
|
216
|
+
assure: ->(v){ _package.versions.include?(v) }
|
217
|
+
switch :force, "Force to uninstall", short: :f
|
218
|
+
action! :uninstall
|
219
|
+
end
|
220
|
+
|
221
|
+
task :uninstall_all do
|
222
|
+
desc "Uninstall all versions"
|
223
|
+
switch :force, "Force to uninstall", short: :f, required: true
|
224
|
+
action! :uninstall_all
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def print_summary(result)
|
229
|
+
result.each do |entry|
|
230
|
+
s = entry[:summary]
|
231
|
+
puts " [#{entry[:hostname]}] #{s[:status]} #{s[:name]} #{s[:installed]}"
|
232
|
+
puts " [#{entry[:hostname]}] #{s[:executable]}" unless s[:executable].nil?
|
233
|
+
puts " [#{entry[:hostname]}] #{s[:alert]}" unless s[:alert].nil?
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
Base = Binary
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
module Package
|
4
|
+
DependencyTypes = %i(before_install after_install)
|
5
|
+
|
6
|
+
class Dependency
|
7
|
+
attr_reader :apply_to
|
8
|
+
attr_reader :type
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :version
|
11
|
+
attr_reader :options
|
12
|
+
|
13
|
+
def initialize(apply_to, type, name, version, **opts)
|
14
|
+
@apply_to = apply_to
|
15
|
+
@type = type
|
16
|
+
@name = name
|
17
|
+
@version = version
|
18
|
+
@options = opts
|
19
|
+
validate
|
20
|
+
end
|
21
|
+
|
22
|
+
def applicable_to?(version)
|
23
|
+
if @apply_to == :all
|
24
|
+
true
|
25
|
+
else
|
26
|
+
Gem::Requirement.new(@apply_to).satisfied_by?(Gem::Version.new(version))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def validate
|
33
|
+
if @apply_to.nil?
|
34
|
+
raise ArgumentError, 'The version requirement that the dependency applies to is NOT provided.'
|
35
|
+
end
|
36
|
+
unless DependencyTypes.include?(@type)
|
37
|
+
raise ArgumentError, "Invalid dependency type: #{type.inspect}"
|
38
|
+
end
|
39
|
+
if @name.nil?
|
40
|
+
raise ArgumentError, 'Dependency name is NOT provided.'
|
41
|
+
end
|
42
|
+
if @version.nil?
|
43
|
+
raise ArgumentError, 'Dependency version is NOT provided.'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
module Package
|
4
|
+
class DependencySet
|
5
|
+
def initialize
|
6
|
+
@dependencies = {}
|
7
|
+
@ctx = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_to(version_requirement, &blk)
|
11
|
+
@ctx = { version_requirement: version_requirement }
|
12
|
+
instance_eval(&blk) if block_given?
|
13
|
+
ensure
|
14
|
+
@ctx = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_install(&blk)
|
18
|
+
if @ctx[:version_requirement].nil?
|
19
|
+
raise RuntimeError, 'Please call #apply_to prior to #before_install.'
|
20
|
+
end
|
21
|
+
@ctx[:type] = :before_install
|
22
|
+
instance_eval(&blk) if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
def after_install(&blk)
|
26
|
+
if @ctx[:version_requirement].nil?
|
27
|
+
raise RuntimeError, 'Please call #apply_to prior to #after_install.'
|
28
|
+
end
|
29
|
+
@ctx[:type] = :after_install
|
30
|
+
instance_eval(&blk) if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def depend_on(name, version:, **opts)
|
34
|
+
if @ctx[:type].nil?
|
35
|
+
raise RuntimeError, 'Please call #before_install or #after_install prior to #depend_on.'
|
36
|
+
end
|
37
|
+
requirement = @ctx[:version_requirement]
|
38
|
+
type = @ctx[:type]
|
39
|
+
dependency = Dependency.new(requirement, type, name, version, **opts)
|
40
|
+
unless @dependencies.has_key?(requirement)
|
41
|
+
@dependencies[requirement] = { before_install: [],
|
42
|
+
after_install: [] }
|
43
|
+
end
|
44
|
+
@dependencies[requirement][type] << dependency
|
45
|
+
end
|
46
|
+
|
47
|
+
def dependencies_for(version, type: nil)
|
48
|
+
types = *type
|
49
|
+
types = DependencyTypes if types.empty?
|
50
|
+
deps = { before_install: [],
|
51
|
+
after_install: [] }
|
52
|
+
@dependencies.each_pair do |r, d|
|
53
|
+
types.each do |t|
|
54
|
+
next if d[t].empty?
|
55
|
+
deps[t] |= d[t] if d[t].first.applicable_to?(version)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
deps
|
59
|
+
end
|
60
|
+
|
61
|
+
def before_install_dependencies_for(version)
|
62
|
+
dependencies_for(version, type: :before_install)
|
63
|
+
end
|
64
|
+
|
65
|
+
def after_install_dependencies_for(version)
|
66
|
+
dependencies_for(version, type: :after_install)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
module Package
|
4
|
+
class Installer < Worker
|
5
|
+
# Shared library file extension name based on OS
|
6
|
+
LibExtensions = {
|
7
|
+
Linux: 'so', # Linux
|
8
|
+
Darwin: 'dylib' # OSX
|
9
|
+
}
|
10
|
+
DefaultLibExtension = 'so'
|
11
|
+
|
12
|
+
attr_reader :configure_opts
|
13
|
+
attr_reader :build_env_vars
|
14
|
+
|
15
|
+
def currently_used_by
|
16
|
+
parent.currently_used_by
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_symlinked?
|
20
|
+
current_package_version == package_version
|
21
|
+
end
|
22
|
+
|
23
|
+
def current_configured?; task.opts.current; end
|
24
|
+
alias_method :current?, :current_configured?
|
25
|
+
|
26
|
+
def current_package_version
|
27
|
+
if symlink?(current_path)
|
28
|
+
File.basename(readlink(current_path))
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_configure_opts
|
35
|
+
@default_configure_opts ||= []
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def init
|
41
|
+
super
|
42
|
+
configure_build_env_variables
|
43
|
+
configure_build_options
|
44
|
+
end
|
45
|
+
|
46
|
+
def configure_build_env_variables
|
47
|
+
@build_env_vars = {
|
48
|
+
ldflags: [ENV['LDFLAGS'].to_s],
|
49
|
+
cflags: [ENV['CFLAGS'].to_s],
|
50
|
+
cppflags: [ENV['CPPFLAGS'].to_s]
|
51
|
+
}
|
52
|
+
if child?
|
53
|
+
configure_parent_build_env_variables
|
54
|
+
configure_parent_build_options
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def configure_parent_build_env_variables
|
59
|
+
parent.build_env_vars[:ldflags] << "-L#{lib_path}"
|
60
|
+
parent.build_env_vars[:cflags] << "-I#{include_path}"
|
61
|
+
parent.build_env_vars[:cppflags] << "-I#{include_path}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def configure_parent_build_options
|
65
|
+
if parent.respond_to?("with_#{package_name}_dir")
|
66
|
+
parent.send("with_#{package_name}_dir", install_path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def compose_build_env_variables
|
71
|
+
build_env_vars.inject({}) do |vars, (k, v)|
|
72
|
+
vars[k] = "'#{v.join(' ').strip}'" unless v.all?(&:empty?)
|
73
|
+
vars
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def configure_build_options
|
78
|
+
@configure_opts = default_configure_opts | (task.opts.__remaining__ || [])
|
79
|
+
@configure_opts.unshift(install_prefix)
|
80
|
+
end
|
81
|
+
|
82
|
+
def compose_build_options
|
83
|
+
@configure_opts.reject(&:empty?).join(' ')
|
84
|
+
end
|
85
|
+
|
86
|
+
def install_prefix
|
87
|
+
"--prefix=#{install_path}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate; end
|
91
|
+
|
92
|
+
def lib_extension
|
93
|
+
@lib_extension ||= LibExtensions.fetch(os_name.to_sym, DefaultLibExtension)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|