luban 0.2.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.
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