luban 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +37 -0
  6. data/Rakefile +1 -0
  7. data/bin/console +14 -0
  8. data/bin/setup +7 -0
  9. data/exe/luban +3 -0
  10. data/lib/luban/deployment/cli/application/authenticator.rb +106 -0
  11. data/lib/luban/deployment/cli/application/base.rb +179 -0
  12. data/lib/luban/deployment/cli/application/builder.rb +67 -0
  13. data/lib/luban/deployment/cli/application/publisher.rb +215 -0
  14. data/lib/luban/deployment/cli/application/repository.rb +175 -0
  15. data/lib/luban/deployment/cli/application/scm/git.rb +49 -0
  16. data/lib/luban/deployment/cli/application/scm/rsync.rb +47 -0
  17. data/lib/luban/deployment/cli/application.rb +5 -0
  18. data/lib/luban/deployment/cli/command.rb +360 -0
  19. data/lib/luban/deployment/cli/package/binary.rb +241 -0
  20. data/lib/luban/deployment/cli/package/dependency.rb +49 -0
  21. data/lib/luban/deployment/cli/package/dependency_set.rb +71 -0
  22. data/lib/luban/deployment/cli/package/installer/core.rb +98 -0
  23. data/lib/luban/deployment/cli/package/installer/install.rb +330 -0
  24. data/lib/luban/deployment/cli/package/installer/paths.rb +81 -0
  25. data/lib/luban/deployment/cli/package/installer.rb +3 -0
  26. data/lib/luban/deployment/cli/package/service.rb +17 -0
  27. data/lib/luban/deployment/cli/package/worker.rb +43 -0
  28. data/lib/luban/deployment/cli/package.rb +6 -0
  29. data/lib/luban/deployment/cli/project.rb +94 -0
  30. data/lib/luban/deployment/cli.rb +4 -0
  31. data/lib/luban/deployment/configuration/core.rb +67 -0
  32. data/lib/luban/deployment/configuration/filter.rb +54 -0
  33. data/lib/luban/deployment/configuration/question.rb +38 -0
  34. data/lib/luban/deployment/configuration/server.rb +70 -0
  35. data/lib/luban/deployment/configuration/server_set.rb +86 -0
  36. data/lib/luban/deployment/configuration.rb +5 -0
  37. data/lib/luban/deployment/error.rb +5 -0
  38. data/lib/luban/deployment/helpers/configuration.rb +159 -0
  39. data/lib/luban/deployment/helpers/utils.rb +180 -0
  40. data/lib/luban/deployment/helpers.rb +2 -0
  41. data/lib/luban/deployment/packages/bundler.rb +81 -0
  42. data/lib/luban/deployment/packages/git.rb +37 -0
  43. data/lib/luban/deployment/packages/openssl.rb +59 -0
  44. data/lib/luban/deployment/packages/ruby.rb +125 -0
  45. data/lib/luban/deployment/packages/rubygems.rb +89 -0
  46. data/lib/luban/deployment/packages/yaml.rb +33 -0
  47. data/lib/luban/deployment/parameters.rb +160 -0
  48. data/lib/luban/deployment/runner.rb +99 -0
  49. data/lib/luban/deployment/templates/envrc.erb +30 -0
  50. data/lib/luban/deployment/templates/unset_envrc.erb +28 -0
  51. data/lib/luban/deployment/version.rb +5 -0
  52. data/lib/luban/deployment/worker/base.rb +71 -0
  53. data/lib/luban/deployment/worker/controller.rb +11 -0
  54. data/lib/luban/deployment/worker/local.rb +19 -0
  55. data/lib/luban/deployment/worker/remote.rb +55 -0
  56. data/lib/luban/deployment/worker/task.rb +25 -0
  57. data/lib/luban/deployment/worker.rb +4 -0
  58. data/lib/luban/deployment.rb +8 -0
  59. data/lib/luban.rb +4 -0
  60. data/luban.gemspec +29 -0
  61. 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