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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f3bb8577686b082a631040457727ce47b0999543
|
4
|
+
data.tar.gz: 657f3f393403953ea5606b288f997f212beb4ab3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7344a5bdf345d666bed583166e90ed40b773fb2f7bdb224102b1d2eefc2de24a270d4b5ae23dbe1eb6fc4a36538ec100d335730134efe25c507474e39b659ff2
|
7
|
+
data.tar.gz: 960aa2296bb31efdae8025a9180188867b94603871977ca307c3b6ac35540d5e99f993115c0e54b38a34849af9e68595d153e399ba25b81461efa3605db0fe8f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Chi Man Lei
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Luban
|
2
|
+
|
3
|
+
Luban is a framework to manage server automation and application deployment.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'luban'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install luban
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Development
|
26
|
+
|
27
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec luban` to use the code located in this directory, ignoring other installed copies of this gem.
|
28
|
+
|
29
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it ( https://github.com/[my-github-username]/luban/fork )
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "luban"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/luban
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
class Application
|
4
|
+
class Authenticator < Luban::Deployment::Worker::Base
|
5
|
+
def private_key_file_name
|
6
|
+
@private_key_file_name ||= "id_#{authen_key_type}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def authen_keys_path
|
10
|
+
@authen_keys_path ||= Pathname.new(user_home).join('.ssh')
|
11
|
+
end
|
12
|
+
|
13
|
+
def private_key_file_path
|
14
|
+
@private_key_file ||= authen_keys_path.join(private_key_file_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def public_key_file_path
|
18
|
+
@public_key_file_path ||= authen_keys_path.join("#{private_key_file_name}.pub")
|
19
|
+
end
|
20
|
+
|
21
|
+
def authorized_keys_file_path
|
22
|
+
@authorized_keys_file_path ||= authen_keys_path.join('authorized_keys')
|
23
|
+
end
|
24
|
+
|
25
|
+
def public_key
|
26
|
+
@public_key ||= get_public_key
|
27
|
+
end
|
28
|
+
|
29
|
+
def keyget_command
|
30
|
+
@keyget_command ||= "cat #{public_key_file_path} 2>&1"
|
31
|
+
end
|
32
|
+
|
33
|
+
def keygen_command
|
34
|
+
@keygen_command ||= "ssh-keygen -t #{authen_key_type} -f #{private_key_file_path} -N '' 2>&1"
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_public_key
|
38
|
+
generate_key_pairs
|
39
|
+
capture(keyget_command)
|
40
|
+
end
|
41
|
+
|
42
|
+
def generate_key_pairs
|
43
|
+
execute(keygen_command) unless key_pairs_generated?
|
44
|
+
end
|
45
|
+
|
46
|
+
def key_pairs_generated?
|
47
|
+
file?(private_key_file_path) and file?(public_key_file_path)
|
48
|
+
end
|
49
|
+
|
50
|
+
def app; task.opts.app; end
|
51
|
+
|
52
|
+
def promptless_authen
|
53
|
+
if promptless_authen_enabled?
|
54
|
+
update_result "Skipped! Promptless authentication has been enabled ALREADY.",
|
55
|
+
status: :skipped, public_key: public_key
|
56
|
+
else
|
57
|
+
setup_password_authen
|
58
|
+
generate_key_pairs
|
59
|
+
add_authorized_keys
|
60
|
+
update_result "Promptless authentication is enabled.", public_key: public_key
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def promptless_authen_enabled?
|
65
|
+
origin_auth_methods = host.ssh_options[:auth_methods]
|
66
|
+
host.ssh_options[:auth_methods] = %w(publickey)
|
67
|
+
capture('echo ok') == 'ok'
|
68
|
+
rescue Net::SSH::AuthenticationFailed
|
69
|
+
false
|
70
|
+
ensure
|
71
|
+
if origin_auth_methods.nil?
|
72
|
+
host.ssh_options.delete(:auth_methods)
|
73
|
+
else
|
74
|
+
host.ssh_options[:auth_methods] = origin_auth_methods
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def setup_password_authen
|
81
|
+
host.user, host.password = user, nil if host.user.nil?
|
82
|
+
host.password = app.password_for(host.user) if host.password.nil?
|
83
|
+
host.ssh_options[:auth_methods] = %w(keyboard-interactive)
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_authorized_keys
|
87
|
+
public_keys = task.opts.public_keys || []
|
88
|
+
public_keys.uniq!
|
89
|
+
if file?(authorized_keys_file_path)
|
90
|
+
public_keys.each { |k| add_authorized_key(k) unless key_authorized?(k) }
|
91
|
+
else
|
92
|
+
public_keys.each { |k| add_authorized_key(k) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def add_authorized_key(key)
|
97
|
+
execute("umask 077; echo #{key} >> #{authorized_keys_file_path} 2>&1")
|
98
|
+
end
|
99
|
+
|
100
|
+
def key_authorized?(key)
|
101
|
+
test("grep -v \"^#\" #{authorized_keys_file_path} | grep -Fxq \"#{key}\"")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
class Application < Luban::Deployment::Command
|
4
|
+
include Luban::Deployment::Parameters::Project
|
5
|
+
include Luban::Deployment::Parameters::Application
|
6
|
+
include Luban::Deployment::Command::Tasks::Install
|
7
|
+
include Luban::Deployment::Command::Tasks::Deploy
|
8
|
+
include Luban::Deployment::Command::Tasks::Control
|
9
|
+
|
10
|
+
attr_reader :packages
|
11
|
+
attr_reader :services
|
12
|
+
|
13
|
+
def has_source?; !source.empty?; end
|
14
|
+
def has_profile?; !profile.empty?; end
|
15
|
+
def has_packages?; !packages.empty?; end
|
16
|
+
def has_services?; !services.empty?; end
|
17
|
+
|
18
|
+
def installable?; has_source? or has_packages?; end
|
19
|
+
def deployable?; has_source? or has_profile? or has_services?; end
|
20
|
+
def controllable?; has_source? or has_profile? or has_services?; end
|
21
|
+
|
22
|
+
def use_package?(package_name, package_version, servers: [])
|
23
|
+
package_name = package_name.to_sym
|
24
|
+
packages.has_key?(package_name) and
|
25
|
+
packages[package_name].has_version?(package_version) and
|
26
|
+
packages[package_name].config.servers.any? { |s| servers.include?(s) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def other_package_users_for(package_name, package_version, servers: [])
|
30
|
+
parent.package_users_for(package_name, package_version,
|
31
|
+
exclude: [name], servers: servers)
|
32
|
+
end
|
33
|
+
|
34
|
+
def package(name, version:, **opts)
|
35
|
+
yield opts if block_given?
|
36
|
+
name = name.to_sym
|
37
|
+
pkg = if has_command?(name)
|
38
|
+
commands[name]
|
39
|
+
else
|
40
|
+
command(name, base: Luban::Deployment::Package::Base.package_class(name))
|
41
|
+
end
|
42
|
+
pkg.update_package_options(version, opts)
|
43
|
+
services[name] = pkg if pkg.is_a?(Luban::Deployment::Package::Service)
|
44
|
+
packages[name] = pkg
|
45
|
+
end
|
46
|
+
alias_method :require_package, :package
|
47
|
+
|
48
|
+
def source(from = nil, **opts)
|
49
|
+
from.nil? ? @source : (@source = opts.merge(name: 'app', from: from))
|
50
|
+
end
|
51
|
+
|
52
|
+
def profile(from = nil, **opts)
|
53
|
+
from.nil? ? @profile : (@profile = opts.merge(name: 'profile', from: from))
|
54
|
+
end
|
55
|
+
|
56
|
+
def password_for(user); parent.password_for(user); end
|
57
|
+
|
58
|
+
def promptless_authen(args:, opts:)
|
59
|
+
opts = opts.merge(app: self, public_keys: Array(public_key!(args: args, opts: opts)))
|
60
|
+
opts[:roles] << scm_role unless scm_role.nil?
|
61
|
+
promptless_authen!(args: args, opts: opts)
|
62
|
+
end
|
63
|
+
|
64
|
+
def build(args:, opts:)
|
65
|
+
show_app_environment
|
66
|
+
promptless_authen(args: args, opts: opts)
|
67
|
+
build_repositories(args: args, opts: opts)
|
68
|
+
build!(args: args, opts: opts)
|
69
|
+
install_all(args: args, opts: opts)
|
70
|
+
end
|
71
|
+
|
72
|
+
def destroy(args:, opts:)
|
73
|
+
uninstall_all(args: args, opts: opts)
|
74
|
+
destroy!(args: args, opts: opts)
|
75
|
+
end
|
76
|
+
|
77
|
+
%i(install_all uninstall_all).each do |action|
|
78
|
+
define_method(action) do |args:, opts:|
|
79
|
+
packages.each_value { |p| p.send(__method__, args: args, opts: opts) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
%i(cleanup binstubs show_current show_summary which whence).each do |action|
|
84
|
+
define_method(action) do |args:, opts:|
|
85
|
+
show_app_environment
|
86
|
+
packages.each_value { |p| p.send(__method__, args: args, opts: opts) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
alias_method :cleanup_packages, :cleanup
|
91
|
+
def cleanup(args:, opts:)
|
92
|
+
cleanup_packages(args: args, opts: opts)
|
93
|
+
cleanup!(args: args, opts: opts)
|
94
|
+
end
|
95
|
+
|
96
|
+
def destroy_project(args:, opts:)
|
97
|
+
show_app_environment
|
98
|
+
destroy!(args: args, opts: opts.merge(destroy_project: true))
|
99
|
+
end
|
100
|
+
|
101
|
+
def deploy(args:, opts:)
|
102
|
+
show_app_environment
|
103
|
+
deploy_releases(args: args, opts: opts)
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
def set_parameters
|
109
|
+
super
|
110
|
+
copy_parameters_from_parent(:stage, :project)
|
111
|
+
@packages = {}
|
112
|
+
@services = {}
|
113
|
+
@source = {}
|
114
|
+
@profile = {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def validate_parameters
|
118
|
+
super
|
119
|
+
validate_project_parameters
|
120
|
+
validate_application_parameters
|
121
|
+
end
|
122
|
+
|
123
|
+
def set_default_parameters
|
124
|
+
super
|
125
|
+
set_default_project_parameters
|
126
|
+
set_default :application, self.class.name.split(':').last.downcase
|
127
|
+
set_default_application_parameters
|
128
|
+
set_default_profile
|
129
|
+
end
|
130
|
+
|
131
|
+
def set_default_profile
|
132
|
+
profile_path = config_finder[:application].stage_config_path.join('profile')
|
133
|
+
profile(profile_path, scm: :rsync) if profile_path.directory?
|
134
|
+
end
|
135
|
+
|
136
|
+
def load_configuration
|
137
|
+
config_finder[:project].load_configuration
|
138
|
+
config_finder[:application].load_configuration
|
139
|
+
end
|
140
|
+
|
141
|
+
def setup_descriptions
|
142
|
+
desc "Manage application #{display_name}"
|
143
|
+
long_desc "Manage the deployment of application #{display_name} in #{parent.class.name}"
|
144
|
+
end
|
145
|
+
|
146
|
+
def show_app_environment
|
147
|
+
puts "#{display_name} in #{parent.class.name}"
|
148
|
+
end
|
149
|
+
|
150
|
+
%i(build destroy cleanup).each do |m|
|
151
|
+
define_task_method("#{m}!", task: m, worker: :builder)
|
152
|
+
end
|
153
|
+
|
154
|
+
def build_repositories(args:, opts:)
|
155
|
+
build_repository!(args: args, opts: opts.merge(repository: profile)) if has_profile?
|
156
|
+
build_repository!(args: args, opts: opts.merge(repository: source)) if has_source?
|
157
|
+
end
|
158
|
+
|
159
|
+
def deploy_releases(args:, opts:)
|
160
|
+
deploy_release(args: args, opts: opts.merge(repository: profile)) if has_profile?
|
161
|
+
deploy_release(args: args, opts: opts.merge(repository: source)) if has_source?
|
162
|
+
end
|
163
|
+
|
164
|
+
def deploy_release(args:, opts:)
|
165
|
+
package_release!(args: args, opts: opts)[:release].tap do |release|
|
166
|
+
unless release.nil?
|
167
|
+
publish_release!(args: args, opts: opts.merge(release: release))
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
define_task_method("promptless_authen!", task: :promptless_authen, worker: :authenticator)
|
173
|
+
define_task_method("public_key!", task: :public_key, worker: :authenticator, locally: true)
|
174
|
+
define_task_method("build_repository!", task: :build, worker: :repository, locally: true)
|
175
|
+
define_task_method("package_release!", task: :package, worker: :repository, locally: true)
|
176
|
+
define_task_method("publish_release!", task: :publish, worker: :publisher)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
class Application
|
4
|
+
class Builder < Luban::Deployment::Worker::Remote
|
5
|
+
def envrc_template_file
|
6
|
+
@envrc_template_file ||= find_template_file("envrc.erb")
|
7
|
+
end
|
8
|
+
|
9
|
+
def unset_envrc_template_file
|
10
|
+
@unset_envrc_template_file ||= find_template_file("unset_envrc.erb")
|
11
|
+
end
|
12
|
+
|
13
|
+
def build
|
14
|
+
bootstrap
|
15
|
+
create_envrc_files
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy
|
19
|
+
task.opts.destroy_project ? destroy_project : destroy_app
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy_project
|
23
|
+
rm(etc_path.join('*', "#{stage}.#{project}.*"))
|
24
|
+
rmdir(project_path)
|
25
|
+
update_result "The project environment is destroyed."
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy_app
|
29
|
+
rm(etc_path.join('*', "#{stage}.#{project}.#{application}.*"))
|
30
|
+
rm(app_path)
|
31
|
+
update_result "The application environment is destroyed."
|
32
|
+
end
|
33
|
+
|
34
|
+
def cleanup
|
35
|
+
execute("find #{tmp_path}/* -type f|xargs rm -f")
|
36
|
+
update_result "Temporary files in app environment is cleaned up."
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def bootstrap
|
42
|
+
assure_dirs(etc_path, tmp_path,
|
43
|
+
app_bin_path, app_tmp_path,
|
44
|
+
releases_path, shared_path)
|
45
|
+
assure_linked_dirs
|
46
|
+
end
|
47
|
+
|
48
|
+
def assure_linked_dirs
|
49
|
+
return if linked_dirs.empty?
|
50
|
+
linked_dirs.each do |dir|
|
51
|
+
linked_dir = shared_path.join(dir)
|
52
|
+
assure(:directory, linked_dir) { mkdir(linked_dir) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_envrc_files
|
57
|
+
upload_by_template(file_to_upload: envrc_file,
|
58
|
+
template_file: envrc_template_file,
|
59
|
+
auto_revision: true)
|
60
|
+
upload_by_template(file_to_upload: unset_envrc_file,
|
61
|
+
template_file: unset_envrc_template_file,
|
62
|
+
auto_revision: true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module Luban
|
2
|
+
module Deployment
|
3
|
+
class Application
|
4
|
+
class Publisher < Luban::Deployment::Worker::Remote
|
5
|
+
def release_name; task.opts.release[:name]; end
|
6
|
+
def release_tag; task.opts.release[:tag]; end
|
7
|
+
def release_package_path; task.opts.release[:path]; end
|
8
|
+
def release_md5; task.opts.release[:md5]; end
|
9
|
+
def bundled_gems; task.opts.release[:bundled_gems]; end
|
10
|
+
def locked_gemfile; bundled_gems[:locked_gemfile]; end
|
11
|
+
def gems_source; bundled_gems[:gems_cache]; end
|
12
|
+
def gems; bundled_gems[:gems]; end
|
13
|
+
|
14
|
+
def publish_app?; release_name == 'app'; end
|
15
|
+
def publish_profile?; release_name == 'profile'; end
|
16
|
+
|
17
|
+
def display_name
|
18
|
+
@display_name ||= "#{application} #{release_name} (release: #{release_tag})"
|
19
|
+
end
|
20
|
+
|
21
|
+
def releases_path
|
22
|
+
@releases_path ||= super.join(release_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def release_path
|
26
|
+
@release_path ||= releases_path.join(release_tag)
|
27
|
+
end
|
28
|
+
|
29
|
+
def releases_log_path
|
30
|
+
@releases_log_path ||= app_path.join('releases.log')
|
31
|
+
end
|
32
|
+
|
33
|
+
def gemfile
|
34
|
+
@gemfile ||= release_path.join('Gemfile')
|
35
|
+
end
|
36
|
+
|
37
|
+
def bundle_cmd
|
38
|
+
@bundle_cmd ||= app_bin_path.join('bundle')
|
39
|
+
end
|
40
|
+
|
41
|
+
def bundle_config_path
|
42
|
+
@bundle_config_path ||= shared_path.join('.bundle')
|
43
|
+
end
|
44
|
+
|
45
|
+
def bundle_path
|
46
|
+
@bundle_path ||= shared_path.join('vendor').join('bundle')
|
47
|
+
end
|
48
|
+
|
49
|
+
def gems_cache_path
|
50
|
+
@gems_cache_path ||= shared_path.join('vendor').join('cache')
|
51
|
+
end
|
52
|
+
|
53
|
+
def bundle_without
|
54
|
+
@bundle_without ||= %w(development test)
|
55
|
+
end
|
56
|
+
|
57
|
+
def bundle_flags
|
58
|
+
@bundle_flags ||= %w(--deployment --quiet)
|
59
|
+
end
|
60
|
+
|
61
|
+
def bundle_linked_dirs
|
62
|
+
@bundle_linked_dirs ||= %w(.bundle vendor/cache vendor/bundle)
|
63
|
+
end
|
64
|
+
|
65
|
+
def published?
|
66
|
+
get_releases.include?(release_tag)
|
67
|
+
end
|
68
|
+
|
69
|
+
def publish
|
70
|
+
assure_dirs(releases_path)
|
71
|
+
if published?
|
72
|
+
if force?
|
73
|
+
publish!
|
74
|
+
else
|
75
|
+
update_result "Skipped! #{display_name} has been published ALREADY.", status: :skipped
|
76
|
+
return
|
77
|
+
end
|
78
|
+
else
|
79
|
+
publish!
|
80
|
+
end
|
81
|
+
|
82
|
+
if published?
|
83
|
+
update_result "Successfully published #{display_name}."
|
84
|
+
else
|
85
|
+
update_result "Failed to publish #{display_name}", status: :failed, level: :error
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def after_publish
|
90
|
+
create_symlinks
|
91
|
+
bundle_gems
|
92
|
+
end
|
93
|
+
|
94
|
+
protected
|
95
|
+
|
96
|
+
def get_releases
|
97
|
+
capture(:ls, '-xt', releases_path).split
|
98
|
+
end
|
99
|
+
|
100
|
+
def publish!
|
101
|
+
rollout_release
|
102
|
+
cleanup_releases
|
103
|
+
end
|
104
|
+
|
105
|
+
def rollout_release
|
106
|
+
upload_to = app_tmp_path.join(release_package_path.basename)
|
107
|
+
upload!(release_package_path.to_s, upload_to.to_s)
|
108
|
+
if md5_matched?(upload_to, release_md5) and
|
109
|
+
test(:tar, "-xzf #{upload_to} -C #{releases_path}")
|
110
|
+
create_symlinks
|
111
|
+
update_releases_log
|
112
|
+
else
|
113
|
+
rm(release_path)
|
114
|
+
end
|
115
|
+
ensure
|
116
|
+
rm(upload_to)
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_symlinks
|
120
|
+
send("create_#{release_name}_symlinks")
|
121
|
+
end
|
122
|
+
|
123
|
+
def create_profile_symlinks
|
124
|
+
create_release_symlink(shared_path)
|
125
|
+
create_shared_symlinks_for(:directory, linked_dirs)
|
126
|
+
create_shared_symlinks_for(:directory, bundle_linked_dirs) if file?(gemfile)
|
127
|
+
end
|
128
|
+
|
129
|
+
def create_app_symlinks
|
130
|
+
create_release_symlink(app_path)
|
131
|
+
create_shared_symlinks_for(:directory, linked_dirs | %w(profile))
|
132
|
+
create_shared_symlinks_for(:directory, bundle_linked_dirs) if file?(gemfile)
|
133
|
+
create_shared_symlinks_for(:file, linked_files)
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_release_symlink(target_dir)
|
137
|
+
assure_symlink(release_path, target_dir.join(release_name))
|
138
|
+
end
|
139
|
+
|
140
|
+
def create_shared_symlinks_for(type, linked_paths)
|
141
|
+
linked_paths.each do |path|
|
142
|
+
target_path = release_path.join(path)
|
143
|
+
assure_dirs(target_path.dirname)
|
144
|
+
source_path = shared_path.join(path)
|
145
|
+
assure_symlink(source_path, target_path)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def update_releases_log
|
150
|
+
execute %{echo "[$(date -u)][#{user}] #{release_log_message}" >> #{releases_log_path}}
|
151
|
+
end
|
152
|
+
|
153
|
+
def release_log_message
|
154
|
+
"Release #{display_name} in #{stage} #{project} is published successfully."
|
155
|
+
end
|
156
|
+
|
157
|
+
def cleanup_releases
|
158
|
+
releases = get_releases
|
159
|
+
if releases.count > keep_releases
|
160
|
+
releases_to_keep = releases.first(keep_releases)
|
161
|
+
unless releases_to_keep.include?(release_tag)
|
162
|
+
releases_to_keep[-1] = release_tag
|
163
|
+
end
|
164
|
+
releases_to_remove = releases - releases_to_keep
|
165
|
+
releases_to_remove.each do |release|
|
166
|
+
rm(releases_path.join(release))
|
167
|
+
end
|
168
|
+
info "Removed #{releases_to_remove.count} old releases."
|
169
|
+
else
|
170
|
+
info "No old releases to remove (keeping most recent #{keep_releases} releases)."
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def bundle_gems
|
175
|
+
assure_dirs(bundle_config_path, gems_cache_path, bundle_path)
|
176
|
+
sync_gems_cache
|
177
|
+
sync_locked_gemfile
|
178
|
+
install_gems_from_cache
|
179
|
+
end
|
180
|
+
|
181
|
+
def sync_gems_cache
|
182
|
+
capture(:ls, '-xt', gems_cache_path).split.each do |gem_name|
|
183
|
+
rm(gems_cache_path.join(gem_name)) unless gems.has_key?(gem_name)
|
184
|
+
end
|
185
|
+
gems.each_pair do |gem_name, md5|
|
186
|
+
gem_path = gems_cache_path.join(gem_name)
|
187
|
+
unless md5_matched?(gem_path, md5)
|
188
|
+
upload!(gems_source.join(gem_name).to_s, gem_path.to_s)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def sync_locked_gemfile
|
194
|
+
gemfile_lock_path = release_path.join(locked_gemfile[:path].basename)
|
195
|
+
unless md5_matched?(gemfile_lock_path, locked_gemfile[:md5])
|
196
|
+
upload!(locked_gemfile[:path].to_s, gemfile_lock_path.to_s)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def install_gems_from_cache
|
201
|
+
within(release_path) do
|
202
|
+
options = []
|
203
|
+
options << "--gemfile #{gemfile}"
|
204
|
+
options << "--path #{bundle_path}"
|
205
|
+
unless test(bundle_cmd, :check, *options)
|
206
|
+
options << "--without #{bundle_without.join(' ')}"
|
207
|
+
options << bundle_flags.join(' ')
|
208
|
+
execute(bundle_cmd, :install, *options)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|