capistrano-net_storage 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 59881a14b8980789fa4ef926786337ade0361eac
4
+ data.tar.gz: 0214927bf85b177f6ea64492c58cceeb84faab98
5
+ SHA512:
6
+ metadata.gz: 21d9e21f3c3b016195d1268f8df7984d8312238bc5f378360157504892bc84a6395344f57a07c5181b7dc60c7fd22960d6c91ec143e3f9e2f1f6e406ade43e49
7
+ data.tar.gz: 67a53aa1569438d92ee71f7af8b44c153afce5e61412015ee4951e55043baeebec5044cd681f4aa31a2e6a5e92e4f9f505323aaa8a3b56c4477ea0c80912e479
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # Editor Artifacts
12
+ *.sw[pon]
13
+
14
+ # rspec failure tracking
15
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.8
6
+ - 2.2.0
7
+ before_install: gem install bundler -v 1.14.6
8
+ install: bundle _1.14.6_ install
9
+ script: bundle _1.14.6_ exec rake spec
10
+ notifications:
11
+ slack:
12
+ secure: lJCLJetpFwFujt467h250JgTyjF+7qU04Ef6FdwOBZwvjgYmyyZMBhdbL2JOAxrQd6XCWhZy56jCXr4ZdMbMLtzL+wEkQefiOLdpV/556xXK5qmkjLyRWoYOMFEhxSTqjk8hHZVYcLO6IYmwr9CHBWaVGOydADCkR4CYyqmSHtKIbznSs9Y4qrpsf6YC9QiSxoP7d5hayvnVUI8oFvxC8DfPuwrmWoO4WyVTJy2ODg/r/ZNGJCMzW3pvnXGK5oZqgwArlGIV7l3tOX2l24QilkhxhuX7QRCikYoGiIdXqkXdf1S/piaflFGXJ1C2PN5aWEv1/8EYcm7RgPm8I9Pol+7zkpUFUiSj3FDLc/TZZKP7OwPCpXOXZkDTLn0+96H5oWUR+2xwTwETQpW8Qz6ZMiu4EKAiAeU99YgSZOgc3fmonvsQREFwTV1n9AHPFsTHv8f3bzC8gUm3n7PIZX4/vR7Aa+vTVZ1QGW9T7OuWThdCMVVB7L7Q/5pRaE+gFLsN9MQBfbZ5uOOXYHd+yh9rC5qi/wRXPxQ4w1F8R+iKIhF32nUF96jwwCkd7O/LZKlmFRT43App6sfmQIAdPp7Oy/BZZD34u0k5aOciZ59h10+BHmA1E0kphXfbYcbXd53MUCEiYc4HC2xtC5yT+ko+PQ2T37VkkneZIIhjRf+WzXQ=
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.2.0 (2017/4/12)
2
+
3
+ Initial release.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capistrano-net_storage.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 DeNA Co., Ltd., IKEDA Kiyoshi
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,115 @@
1
+ # Capistrano::NetStorage
2
+
3
+ **Capistrano::NetStorage** is a [Capistrano](http://capistranorb.com/) plugin to deploy application
4
+ via _remote storage_ such as [Amazon S3](https://aws.amazon.com/s3/),
5
+ [Google Cloud Storage](https://cloud.google.com/storage/) and so on.
6
+
7
+ Logically, this tool enables _O(1)_ deployment.
8
+
9
+
10
+ ## Concept
11
+
12
+ The image below illustrates the concept of **Capistrano::NetStorage**.
13
+
14
+ ![concept](https://github.com/DeNADev/capistrano-net_storage/raw/resource/images/concept.png)
15
+
16
+ This library goes following procedures as _capistrano tasks_:
17
+
18
+ 1. Prepare an archive of application to upload.
19
+ * Clone or update source code repository on deploy server.
20
+ * Do `bundle install` by an option.
21
+ 2. Upload the archive to _remote storage_.
22
+ 3. Download the archive from _remote storage_ on application servers.
23
+ * This task is executed in parallel way.
24
+
25
+ NOTE:
26
+
27
+ * You need to prepare a _transport class_ to execute upload/download operation suitable for
28
+ _remote storage_. It should inherit `Capistrano::NetStorage::Transport::Base` class.
29
+
30
+ ## Installation
31
+
32
+ Add this line to your application's Gemfile:
33
+
34
+ ```ruby
35
+ gem 'capistrano-net_storage'
36
+ ```
37
+
38
+ And then execute:
39
+
40
+ $ bundle
41
+
42
+ Or install it yourself as:
43
+
44
+ $ gem install capistrano-net_storage
45
+
46
+ ## Configuration
47
+
48
+ Set Capistrano variables by `set name, value`.
49
+
50
+ Name | Default | Description
51
+ ------|---------|------------
52
+ `:scm` | `nil` | Set `:net_storage`
53
+ `:branch` | `master` | Target branch of SCM to release
54
+ `:keep_releases` | `5` | Numbers to keep released versions
55
+ `:net_storage_transport` | `nil` | Transport class for _remote storage_
56
+ `:net_storage_archiver` | `Capistrano::NetStorage::Archiver::Zip` | Archiver class
57
+ `:net_storage_scm` | `Capistrano::NetStorage::SCM::Git` | Internal scm class for application repository
58
+ `:net_storage_with_bundle` | `false` | Do `bundle install` when creating archive
59
+ `:net_storage_config_files` | `nil` | Files to sync `config/` directory on target servers' application directory
60
+ `:net_storage_max_parallels` | number of servers | Max concurrency for remote tasks
61
+ `:net_storage_archive_on_missing` | `true` | If `true`, create and upload archive only when target archive is missing on remote storage
62
+ `:net_storage_upload_files_by_rsync` | `false` | Use rsync(1) to deploy config files
63
+ `:net_storage_local_base_path` | `.local_repo` | Base directory on deploy server
64
+
65
+ ### Transport Plugins
66
+
67
+ Here are available plugins list which serves as `:net_storage_transport`:
68
+
69
+ - [Capistrano::NetStorage::S3::Transport](https://github.com/DeNADev/capistrano-net_storage-s3)
70
+
71
+ ## Usage
72
+
73
+ Edit Capfile:
74
+
75
+ ```ruby
76
+ # Load DSL and Setup Up Stages
77
+ require 'capistrano/setup'
78
+
79
+ # Includes default deployment tasks
80
+ require 'capistrano/deploy'
81
+
82
+ # Includes tasks from other gems included in your Gemfile
83
+ require 'capistrano/net_storage'
84
+ # Load transport plugin
85
+ # require 'capistrano/net_storage/s3'
86
+ ```
87
+
88
+ Edit your `config/deploy.rb`:
89
+
90
+ ```ruby
91
+ set :scm, :net_storage
92
+ set :net_storage_transport, Your::TransportPluginModule
93
+ # set :net_storage_transport, Capistrano::NetStorage::S3::Transport # w/ capistrano-net_storage-s3
94
+ # set :net_storage_config_files, [your_config_files]
95
+ # set :net_storage_with_bundle, true
96
+ # set :net_storage_archiver, Capistrano::NetStorage::Archiver::TarGzip
97
+ ```
98
+
99
+ ## Example
100
+
101
+ You can see typical usage of this library by
102
+ [capistrano-net_storage_demo](https://github.com/DeNADev/capistrano-net_storage_demo).
103
+
104
+ ## TODO
105
+
106
+ * Support
107
+ [Capistrano SCM plugin system](http://capistranorb.com/documentation/advanced-features/custom-scm/)
108
+ introduced in Capistrano v3.7
109
+
110
+ ## License
111
+
112
+ Available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
113
+
114
+ Copyright (c) 2017 DeNA Co., Ltd., IKEDA Kiyoshi
115
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Open a pry session preloaded with the library'
5
+ task :console do
6
+ sh 'pry --gem'
7
+ end
8
+ task c: :console
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task default: :spec
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'capistrano/net_storage/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'capistrano-net_storage'
8
+ spec.version = Capistrano::NetStorage::VERSION
9
+ spec.authors = ['progrhyme']
10
+
11
+ spec.summary = 'Capistrano SCM Plugin for fast deployment via remote storage'
12
+ spec.description = <<-EODESC
13
+ A Capistrano SCM Plugin to deploy application via remove storage.
14
+ Logically, this enables O(1) deployment.
15
+ EODESC
16
+ spec.homepage = 'https://github.com/DeNADev/capistrano-net_storage'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ spec.require_paths = ['lib']
21
+ spec.required_ruby_version = '>= 2.0'
22
+
23
+ spec.add_runtime_dependency 'capistrano', '>= 3.3.3'
24
+ spec.add_runtime_dependency 'parallel'
25
+ spec.add_runtime_dependency 'bundler'
26
+
27
+ spec.add_development_dependency 'rake'
28
+ spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'pry'
30
+ end
@@ -0,0 +1,20 @@
1
+ require 'capistrano/net_storage/base'
2
+ require 'capistrano/net_storage/error'
3
+ require 'capistrano/net_storage/config'
4
+ require 'capistrano/net_storage/coordinator'
5
+ require 'capistrano/net_storage/utils'
6
+ require 'capistrano/net_storage/bundler'
7
+ require 'capistrano/net_storage/cleaner'
8
+ require 'capistrano/net_storage/archiver/zip'
9
+ require 'capistrano/net_storage/archiver/tar_gzip'
10
+ require 'capistrano/net_storage/scm/git'
11
+ require 'capistrano/net_storage/version'
12
+
13
+ # See capistrano/net_storage/base.rb
14
+ Capistrano::NetStorage.setup! do
15
+ config = Capistrano::NetStorage::Config.new
16
+ {
17
+ config: config,
18
+ coordinator: Capistrano::NetStorage::Coordinator.new(config),
19
+ }
20
+ end
@@ -0,0 +1,28 @@
1
+ module Capistrano
2
+ module NetStorage
3
+ module Archiver
4
+ # Abstract class to archive and extract whole application contents
5
+ # @abstract
6
+ class Base
7
+ # Check prerequisites to archive
8
+ # @abstract
9
+ def check
10
+ raise NotImplementedError
11
+ end
12
+
13
+ # Create archive
14
+ # @abstract
15
+ def archive
16
+ raise NotImplementedError
17
+ end
18
+
19
+ # Extract archive.
20
+ # Archive file should be cleaned up after extracted.
21
+ # @abstract
22
+ def extract
23
+ raise NotImplementedError
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ require 'capistrano/net_storage/archiver/base'
2
+ require 'capistrano/net_storage/utils'
3
+
4
+ # Archiver class for .tar.gz format
5
+ class Capistrano::NetStorage::Archiver::TarGzip < Capistrano::NetStorage::Archiver::Base
6
+ include Capistrano::NetStorage::Utils
7
+
8
+ def check
9
+ on :local do
10
+ execute :which, 'tar'
11
+ end
12
+ end
13
+
14
+ def archive
15
+ c = config
16
+ on :local do
17
+ within c.local_release_path do
18
+ execute :tar, 'czf', c.local_archive_path, '.'
19
+ end
20
+ end
21
+ end
22
+
23
+ def extract
24
+ c = config
25
+ on c.servers, in: :groups, limit: c.max_parallels do
26
+ execute :mkdir, '-p', release_path
27
+ within release_path do
28
+ execute :tar, 'xzf', c.archive_path
29
+ execute :rm, '-f', c.archive_path
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,30 @@
1
+ require 'capistrano/net_storage/archiver/base'
2
+ require 'capistrano/net_storage/utils'
3
+
4
+ # Archiver class for zip format
5
+ class Capistrano::NetStorage::Archiver::Zip < Capistrano::NetStorage::Archiver::Base
6
+ include Capistrano::NetStorage::Utils
7
+
8
+ def check
9
+ on :local do
10
+ execute :which, 'zip'
11
+ end
12
+ end
13
+
14
+ def archive
15
+ c = config
16
+ on :local do
17
+ within c.local_release_path do
18
+ execute :zip, c.local_archive_path, '-r', '.'
19
+ end
20
+ end
21
+ end
22
+
23
+ def extract
24
+ c = config
25
+ on c.servers, in: :groups, limit: c.max_parallels do
26
+ execute :unzip, c.archive_path, '-d', release_path
27
+ execute :rm, '-f', c.archive_path
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require 'forwardable'
2
+
3
+ module Capistrano
4
+ module NetStorage
5
+ class << self
6
+ attr_reader :config, :coordinator
7
+
8
+ extend Forwardable
9
+ def_delegators :coordinator, :archiver, :scm, :cleaner, :bundler, :transport
10
+ end
11
+
12
+ def self.setup!(params = nil)
13
+ params ||= yield
14
+ @config = params[:config]
15
+ @coordinator = params[:coordinator]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,67 @@
1
+ require 'bundler'
2
+ require 'capistrano/net_storage/utils'
3
+
4
+ module Capistrano
5
+ module NetStorage
6
+ class Bundler
7
+ include Capistrano::NetStorage::Utils
8
+
9
+ def check
10
+ on :local do
11
+ execute :which, 'bundle'
12
+ end
13
+ end
14
+
15
+ # Do bundle install locally. Installed gems are to be included to the release.
16
+ def install
17
+ c = config
18
+ on :local do
19
+ local_release_bundle_path = c.local_release_path.join('vendor', 'bundle')
20
+ execute :mkdir, '-p', local_release_bundle_path
21
+ execute :mkdir, '-p', "#{c.local_release_path}/.bundle"
22
+ # Copy shared gems to release bundle path beforehand to reuse installed previously
23
+ execute :rsync, '-a', "#{c.local_bundle_path}/", "#{local_release_bundle_path}/"
24
+
25
+ within c.local_release_path do
26
+ ::Bundler.with_clean_env do
27
+ install_options = %W(
28
+ --gemfile #{c.local_release_path}/Gemfile --deployment --quiet
29
+ --path #{local_release_bundle_path} --without development test
30
+ )
31
+ execute :bundle, 'install', *install_options
32
+ execute :bundle, 'clean'
33
+ # Sync installed gems to shared directory to reuse them next time
34
+ rsync_options = %W(-a --delete #{local_release_bundle_path}/ #{c.local_bundle_path}/)
35
+ execute :rsync, *rsync_options
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ # Create +.bundle/config+ at release path on remote servers
42
+ def sync_config
43
+ c = config
44
+
45
+ on c.servers, in: :groups, limit: c.max_parallels do
46
+ within release_path do
47
+ execute :mkdir, '-p', '.bundle'
48
+ end
49
+ end
50
+
51
+ bundle_config_path = "#{c.local_base_path}/bundle_config"
52
+ File.open(bundle_config_path, 'w') do |file|
53
+ file.print(<<-EOS)
54
+ ---
55
+ BUNDLE_FROZEN: "1"
56
+ BUNDLE_PATH: "#{release_path.join('vendor', 'bundle')}"
57
+ BUNDLE_WITHOUT: "development:test"
58
+ BUNDLE_DISABLE_SHARED_GEMS: "true"
59
+ BUNDLE_BIN: "#{release_path.join('bin')}"
60
+ EOS
61
+ end
62
+
63
+ upload_files([bundle_config_path], dest_path: release_path.join('.bundle', 'config'))
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,30 @@
1
+ require 'capistrano/net_storage/utils'
2
+
3
+ module Capistrano
4
+ module NetStorage
5
+ # Executor class for cleaning tasks
6
+ class Cleaner
7
+ include Capistrano::NetStorage::Utils
8
+
9
+ # Clean up local release directories and archives.
10
+ # Assumes they are under +config.local_releases_path+
11
+ # @see Capistrano::NetStorage::Config#local_releases_path
12
+ def cleanup_local_release
13
+ c = config
14
+ on :local do
15
+ releases = capture(:ls, '-xtr', c.local_releases_path).split
16
+ # Contains archive files and extracted directories
17
+ if releases.count > fetch(:keep_releases) * 2
18
+ info "Keeping #{fetch(:keep_releases)} * 2 of #{releases.count} local releases"
19
+ olds_str = (releases - releases.last(fetch(:keep_releases) * 2)).map do |file|
20
+ c.local_releases_path.join(file)
21
+ end.join(' ')
22
+ execute :rm, '-rf', olds_str
23
+ else
24
+ info "No old local releases in #{c.local_releases_path}"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,163 @@
1
+ require 'capistrano/net_storage/error'
2
+ require 'capistrano/net_storage/bundler'
3
+ require 'capistrano/net_storage/archiver/zip'
4
+ require 'capistrano/net_storage/scm/git'
5
+
6
+ module Capistrano
7
+ module NetStorage
8
+ class Config
9
+ def executor_class(type)
10
+ @executor_classes ||= {}
11
+ @executor_classes[type] ||= fetch(:"net_storage_#{type}")
12
+ @executor_classes[type] ||= begin
13
+ case type
14
+ when :archiver
15
+ Capistrano::NetStorage::Archiver::Zip
16
+ when :scm
17
+ Capistrano::NetStorage::SCM::Git
18
+ when :cleaner
19
+ Capistrano::NetStorage::Cleaner
20
+ when :bundler
21
+ Capistrano::NetStorage::Bundler
22
+ when :transport
23
+ msg = 'You have to set :net_storage_transport because no default transport class!'
24
+ raise Capistrano::NetStorage::Error, msg
25
+ else
26
+ raise "Unknown type! #{type}"
27
+ end
28
+ end
29
+ end
30
+
31
+ # Servers to deploy
32
+ def servers
33
+ fetch(:net_storage_servers, -> { release_roles(:all) })
34
+ end
35
+
36
+ def max_parallels
37
+ @max_parallels ||= fetch(:net_storage_max_parallels, servers.size)
38
+ end
39
+
40
+ # Application configuration files to be deployed with
41
+ # @return [Array<String, Pathname>]
42
+ def config_files
43
+ @config_files ||= fetch(:net_storage_config_files)
44
+ end
45
+
46
+ # If +true+, skip to bundle gems bundled with target app.
47
+ # Defaults to +true+
48
+ def skip_bundle?
49
+ @has_checked_skip_bundle ||= begin
50
+ @skip_bundle = !fetch(:net_storage_with_bundle)
51
+ true
52
+ end
53
+ @skip_bundle
54
+ end
55
+
56
+ # If +true+, create archive ONLY when it's not found on Network Storage.
57
+ # Otherwise, create archive ALWAYS.
58
+ # Defaults to +true+
59
+ def archive_on_missing?
60
+ @has_checked_archive_on_missing ||= begin
61
+ @archive_on_missing = fetch(:net_storage_archive_on_missing, true)
62
+ true
63
+ end
64
+ @archive_on_missing
65
+ end
66
+
67
+ # If +true+, use +rsync+ to sync config files.
68
+ # Otherwise, use +upload!+ by sshkit.
69
+ # Defaults to +false+
70
+ # @see #rsync_options
71
+ def upload_files_by_rsync?
72
+ @upload_files_by_rsync ||= fetch(:net_storage_upload_files_by_rsync, false)
73
+ end
74
+
75
+ # You can set +:user+, +:keys+, +:port+ as ssh options for +rsync+ command to sync configs
76
+ # when +:net_storage_upload_files_by_rsync+ is set +true+.
77
+ # @see #upload_files_by_rsync?
78
+ def rsync_options
79
+ @rsync_options ||= fetch(:net_storage_rsync_options, fetch(:ssh_options, {}))
80
+ end
81
+
82
+ #
83
+ # Path settings
84
+ #
85
+
86
+ # Path of base directory on localhost
87
+ # @return [Pathname]
88
+ def local_base_path
89
+ @local_base_path ||= pathname(fetch(:net_storage_local_base_path, "#{Dir.pwd}/.local_repo"))
90
+ end
91
+
92
+ # Path to clone repository on localhost
93
+ # @return [Pathname]
94
+ def local_mirror_path
95
+ @local_mirror_path ||= pathname(fetch(:net_storage_local_mirror_path))
96
+ @local_mirror_path ||= local_base_path.join('mirror')
97
+ end
98
+
99
+ # Path to keep release directories and archives on localhost
100
+ # @return [Pathname]
101
+ def local_releases_path
102
+ @local_releases_path ||= pathname(fetch(:net_storage_local_releases_path))
103
+ @local_releases_path ||= local_base_path.join('releases')
104
+ end
105
+
106
+ # Path to take a snapshot of repository for release
107
+ # @return [Pathname]
108
+ def local_release_path
109
+ @local_release_path ||= pathname(fetch(:net_storage_local_release_path))
110
+ @local_release_path ||= local_releases_path.join(release_timestamp)
111
+ end
112
+
113
+ # Shared directory to install gems
114
+ # @return [Pathname]
115
+ def local_bundle_path
116
+ @local_bundle_path ||= pathname(fetch(:net_storage_local_bundle_path))
117
+ @local_bundle_path ||= local_base_path.join('bundle')
118
+ end
119
+
120
+ # Destination path to archive application on localhost
121
+ # @return [Pathname]
122
+ def local_archive_path
123
+ @local_archive_path ||= pathname(fetch(:net_storage_local_archive_path))
124
+ @local_archive_path ||= pathname("#{local_release_path}.#{archive_suffix}")
125
+ end
126
+
127
+ # Path of archive file to be downloaded on servers
128
+ # @return [Pathname]
129
+ def archive_path
130
+ @archive_path ||= pathname(fetch(:net_storage_archive_path))
131
+ @archive_path ||= begin
132
+ # Set release_timestamp if not set
133
+ fetch(:release_path, set_release_path)
134
+ pathname("#{release_path}.#{archive_suffix}")
135
+ end
136
+ end
137
+
138
+ # Suffix of archive file
139
+ # @return [String]
140
+ def archive_suffix
141
+ case Capistrano::NetStorage.archiver
142
+ when Capistrano::NetStorage::Archiver::Zip
143
+ 'zip'
144
+ when Capistrano::NetStorage::Archiver::TarGzip
145
+ 'tar.gz'
146
+ else
147
+ 'archive'
148
+ end
149
+ end
150
+
151
+ private
152
+
153
+ def pathname(path)
154
+ case path
155
+ when String
156
+ Pathname.new(path)
157
+ else
158
+ path
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,38 @@
1
+ module Capistrano
2
+ module NetStorage
3
+ class Coordinator
4
+ attr_reader :config
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ end
9
+
10
+ def archiver
11
+ load_executor(:archiver)
12
+ end
13
+
14
+ def transport
15
+ load_executor(:transport)
16
+ end
17
+
18
+ def cleaner
19
+ load_executor(:cleaner)
20
+ end
21
+
22
+ def bundler
23
+ load_executor(:bundler)
24
+ end
25
+
26
+ def scm
27
+ load_executor(:scm)
28
+ end
29
+
30
+ private
31
+
32
+ def load_executor(type)
33
+ @executors ||= {}
34
+ @executors[type] ||= config.executor_class(type).new
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ module Capistrano
2
+ module NetStorage
3
+ class Error < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,49 @@
1
+ require 'capistrano/net_storage/utils'
2
+
3
+ module Capistrano
4
+ module NetStorage
5
+ module SCM
6
+ # Base internal SCM class of Capistrano::Netstrage
7
+ # @abstract
8
+ class Base
9
+ include Capistrano::NetStorage::Utils
10
+
11
+ # Check SCM prerequisites
12
+ # @abstract
13
+ def check
14
+ raise NotImplementedError
15
+ end
16
+
17
+ # Clone repository to local
18
+ # @abstract
19
+ def clone
20
+ raise NotImplementedError
21
+ end
22
+
23
+ # Update local repository
24
+ # @abstract
25
+ def update
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # Set current revision to be deployed of the repository
30
+ # @abstract
31
+ def set_current_revision
32
+ raise NotImplementedError
33
+ end
34
+
35
+ # Prepare snapshot of repository to be archived for release
36
+ # @abstract
37
+ def prepare_archive
38
+ raise NotImplementedError
39
+ end
40
+
41
+ # Copy local config files to servers
42
+ def sync_config
43
+ return unless config.config_files
44
+ upload_files(config.config_files, dest_dir: release_path.join('config'))
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,61 @@
1
+ require 'capistrano/net_storage/scm/base'
2
+
3
+ # Internal SCM class for Git repository
4
+ class Capistrano::NetStorage::SCM::Git < Capistrano::NetStorage::SCM::Base
5
+ def check
6
+ on :local do
7
+ execute :git, 'ls-remote', repo_url, 'HEAD'
8
+ end
9
+ end
10
+
11
+ def clone
12
+ c = config
13
+ on :local do
14
+ if File.exist?("#{c.local_mirror_path}/HEAD")
15
+ info t(:mirror_exists, at: c.local_mirror_path)
16
+ else
17
+ execute :git, :clone, '--mirror', repo_url, c.local_mirror_path
18
+ end
19
+ end
20
+ end
21
+
22
+ def update
23
+ c = config
24
+ on :local do
25
+ within c.local_mirror_path do
26
+ execute :git, :remote, :update
27
+ end
28
+ end
29
+ end
30
+
31
+ def set_current_revision
32
+ return if fetch(:current_revision)
33
+ c = config
34
+ on :local do
35
+ within c.local_mirror_path do
36
+ set :current_revision, capture(:git, "rev-parse #{fetch(:branch)}")
37
+ end
38
+ end
39
+ end
40
+
41
+ def prepare_archive
42
+ c = config
43
+ on :local do
44
+ execute :mkdir, '-p', c.local_release_path
45
+
46
+ within c.local_mirror_path do
47
+ if tree = fetch(:repo_tree)
48
+ stripped = tree.slice %r{^/?(.*?)/?$}, 1 # strip both side /
49
+ num_components = stripped.count('/')
50
+ execute(
51
+ :git, :archive, fetch(:branch), tree,
52
+ "| tar -x --strip-components #{num_components} -f - -C ",
53
+ c.local_release_path
54
+ )
55
+ else
56
+ execute :git, :archive, fetch(:branch), '| tar -x -C', c.local_release_path
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ module Capistrano
2
+ module NetStorage
3
+ module Transport
4
+ # Abstract class to transport archive from/to Network Storage
5
+ # @abstract
6
+ class Base
7
+ # Check prerequisites for transport
8
+ # @abstract
9
+ def check
10
+ raise NotImplementedError
11
+ end
12
+
13
+ # Find uploaded archive to be deployed and set +:net_storage_uploaded_archive+ its address
14
+ # @abstract
15
+ def find_uploaded
16
+ raise NotImplementedError
17
+ end
18
+
19
+ # Upload archive onto remote storage
20
+ # @abstract
21
+ def upload
22
+ raise NotImplementedError
23
+ end
24
+
25
+ # Download archive from remote storage to servers.
26
+ # Archive file should be placed at +config.archive_path+
27
+ # @abstract
28
+ def download
29
+ raise NotImplementedError
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ require 'parallel'
2
+
3
+ require 'capistrano/net_storage/base'
4
+
5
+ module Capistrano
6
+ module NetStorage
7
+ # Common utility methods
8
+ module Utils
9
+ private
10
+
11
+ # @see lib/capistrano/net_storage/base.rb
12
+ def config
13
+ Capistrano::NetStorage.config
14
+ end
15
+
16
+ # @param dest_dir [String, Pathname] Destination directory on remote to copy local files into
17
+ # @param dest_path [String, Pathname] Destination file path on remote to copy local files to
18
+ # You can provide either of +dest_dir+ or +dest_path+, or files are copied to same pathes
19
+ def upload_files(files, dest_dir: nil, dest_path: nil)
20
+ c = config
21
+ files.each do |src|
22
+ basename = File.basename(src)
23
+ dest = dest_path || begin
24
+ dir = dest_dir || File.dirname(src)
25
+ File.join(dir, basename)
26
+ end
27
+
28
+ if c.upload_files_by_rsync?
29
+ Parallel.each(c.servers, in_threads: c.max_parallels) do |host|
30
+ ssh = build_ssh_command(host)
31
+ on :local do
32
+ execute :rsync, "-az --rsh='#{ssh}' #{src} #{host}:#{dest}"
33
+ end
34
+ end
35
+ else
36
+ on c.servers, in: :groups, limit: c.max_parallels do
37
+ upload! src, dest
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ # Build ssh command with options for rsync
44
+ def build_ssh_command(host)
45
+ user_opt = ''
46
+ key_opt = ''
47
+ port_opt = ''
48
+ ssh_options = config.rsync_options
49
+
50
+ if user = host.user || ssh_options[:user]
51
+ user_opt = " -l #{user}"
52
+ end
53
+
54
+ if keys = (host.keys.empty? ? ssh_options[:keys] : host.keys)
55
+ keys = keys.is_a?(Array) ? keys : [keys]
56
+ key_opt = keys.map { |key| " -i #{key}" }.join('')
57
+ end
58
+
59
+ if port = host.port || ssh_options[:port]
60
+ port_opt = " -p #{port}"
61
+ end
62
+
63
+ "ssh#{user_opt}#{key_opt}#{port_opt}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module NetStorage
3
+ VERSION = '0.2.0'
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'capistrano/net_storage/all'
2
+ load File.expand_path('../tasks/net_storage.rake', __FILE__)
@@ -0,0 +1,124 @@
1
+ unless defined?(Capistrano::NetStorage::TASK_LOADED) # prevent multiple loads
2
+ Capistrano::NetStorage::TASK_LOADED = true
3
+
4
+ namespace :net_storage do
5
+ # Tasks called by deploy.rake bundled in capistrano core:
6
+ # - :check
7
+ # - :create_release
8
+ # - :set_current_revision
9
+ desc %(Check all components' statuses)
10
+ check_dependencies = %w(archiver transport bundler scm directories).map do |t|
11
+ "net_storage:check:#{t}".to_sym
12
+ end
13
+ task check: check_dependencies
14
+
15
+ desc 'Create and deploy archives via remove storage'
16
+ task create_release: :'net_storage:check' do
17
+ config = Capistrano::NetStorage.config
18
+ skip_upload = false
19
+ if config.archive_on_missing?
20
+ invoke 'net_storage:transport:find_uploaded'
21
+ if fetch(:net_storage_uploaded_archive)
22
+ skip_upload = true
23
+ end
24
+ end
25
+ unless skip_upload
26
+ invoke 'net_storage:upload_archive'
27
+ end
28
+ invoke 'net_storage:pull_deploy'
29
+ end
30
+
31
+ desc 'Set the revision to be deployed'
32
+ task set_current_revision: :'net_storage:scm:update' do
33
+ Capistrano::NetStorage.scm.set_current_revision
34
+ end
35
+
36
+ # Additional tasks for capistrano-net_storage
37
+
38
+ desc 'Deploy config files'
39
+ task :sync_config do
40
+ config = Capistrano::NetStorage.config
41
+ Capistrano::NetStorage.scm.sync_config
42
+ Capistrano::NetStorage.bundler.sync_config unless config.skip_bundle?
43
+ end
44
+ after 'net_storage:create_release', 'net_storage:sync_config'
45
+
46
+ desc 'Clean up local old archives and snapshots'
47
+ task :cleanup_local_release do
48
+ Capistrano::NetStorage.cleaner.cleanup_local_release
49
+ end
50
+ after 'deploy:cleanup', 'net_storage:cleanup_local_release'
51
+
52
+ task prepare_archive: %i(net_storage:scm:update net_storage:check:bundler) do
53
+ config = Capistrano::NetStorage.config
54
+ Capistrano::NetStorage.scm.prepare_archive
55
+ Capistrano::NetStorage.bundler.install unless config.skip_bundle?
56
+ end
57
+
58
+ task create_archive: :'net_storage:prepare_archive' do
59
+ Capistrano::NetStorage.archiver.archive
60
+ end
61
+
62
+ task upload_archive: :'net_storage:create_archive' do
63
+ Capistrano::NetStorage.transport.upload
64
+ end
65
+
66
+ task pull_deploy: :'net_storage:transport:find_uploaded' do
67
+ Capistrano::NetStorage.transport.download
68
+ Capistrano::NetStorage.archiver.extract
69
+ end
70
+
71
+ namespace :check do
72
+ task :archiver do
73
+ Capistrano::NetStorage.archiver.check
74
+ end
75
+
76
+ task :transport do
77
+ Capistrano::NetStorage.transport.check
78
+ end
79
+
80
+ task :bundler do
81
+ config = Capistrano::NetStorage.config
82
+ Capistrano::NetStorage.bundler.check unless config.skip_bundle?
83
+ end
84
+
85
+ task :scm do
86
+ Capistrano::NetStorage.scm.check
87
+ end
88
+
89
+ task :directories do
90
+ config = Capistrano::NetStorage.config
91
+ dirs = [
92
+ config.local_base_path,
93
+ config.local_mirror_path,
94
+ config.local_releases_path,
95
+ ]
96
+ dirs << config.local_bundle_path unless config.skip_bundle?
97
+ on :local do
98
+ dirs.each { |dir| execute :mkdir, '-p', dir }
99
+ end
100
+ end
101
+ end
102
+
103
+ namespace :scm do
104
+ task clone: %i(net_storage:check:scm net_storage:check:directories) do
105
+ Capistrano::NetStorage.scm.clone
106
+ end
107
+
108
+ task update: :'net_storage:scm:clone' do
109
+ Capistrano::NetStorage.scm.update
110
+ end
111
+
112
+ task set_current_revision: :'net_storage:scm:update' do
113
+ Capistrano::NetStorage.scm.set_current_revision
114
+ end
115
+ end
116
+
117
+ namespace :transport do
118
+ task find_uploaded: :'net_storage:scm:set_current_revision' do
119
+ Capistrano::NetStorage.transport.find_uploaded
120
+ end
121
+ end
122
+ end
123
+
124
+ end
File without changes
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-net_storage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - progrhyme
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-04-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capistrano
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.3.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.3.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: parallel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |2
98
+ A Capistrano SCM Plugin to deploy application via remove storage.
99
+ Logically, this enables O(1) deployment.
100
+ email:
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".travis.yml"
108
+ - CHANGELOG.md
109
+ - Gemfile
110
+ - LICENSE.txt
111
+ - README.md
112
+ - Rakefile
113
+ - capistrano-net_storage.gemspec
114
+ - lib/capistrano-net_storage.rb
115
+ - lib/capistrano/net_storage.rb
116
+ - lib/capistrano/net_storage/all.rb
117
+ - lib/capistrano/net_storage/archiver/base.rb
118
+ - lib/capistrano/net_storage/archiver/tar_gzip.rb
119
+ - lib/capistrano/net_storage/archiver/zip.rb
120
+ - lib/capistrano/net_storage/base.rb
121
+ - lib/capistrano/net_storage/bundler.rb
122
+ - lib/capistrano/net_storage/cleaner.rb
123
+ - lib/capistrano/net_storage/config.rb
124
+ - lib/capistrano/net_storage/coordinator.rb
125
+ - lib/capistrano/net_storage/error.rb
126
+ - lib/capistrano/net_storage/scm/base.rb
127
+ - lib/capistrano/net_storage/scm/git.rb
128
+ - lib/capistrano/net_storage/transport/base.rb
129
+ - lib/capistrano/net_storage/utils.rb
130
+ - lib/capistrano/net_storage/version.rb
131
+ - lib/capistrano/tasks/net_storage.rake
132
+ homepage: https://github.com/DeNADev/capistrano-net_storage
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '2.0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.6.11
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Capistrano SCM Plugin for fast deployment via remote storage
156
+ test_files: []