capistrano-net_storage 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.
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: []