nanoc-deploying 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ab7916826cd210ddc7bfacca3b6798df30ce86a5945cd1848e7ec4b6ed4d2dd5
4
+ data.tar.gz: '059736d6529ced037de1e7cebd6aa7071ab1d0213a2dae47f6b1e1b46c41dabc'
5
+ SHA512:
6
+ metadata.gz: f02b27a4e978eb3edc2b339e07924defdc7643953c03e61035916fc8eb934b00a16a5537ae81e8cef6658187dc71a81e09c095ba230fc1be4897814fd6667ff9
7
+ data.tar.gz: 40a9a102559188234e1185d9a23c5f4bd23458bf88a0a0b6a09eec2f58dcdaa845836da937a0b1bfd0ee15a679d40a4db5ba378ed2c63b2c156ab10704025007
data/NEWS.md ADDED
@@ -0,0 +1,5 @@
1
+ # nanoc-deploying news
2
+
3
+ ## 1.0.0 (2020-03-07)
4
+
5
+ Initial release (extracted from nanoc)
@@ -0,0 +1,5 @@
1
+ # nanoc-deploying
2
+
3
+ This provides the `deploy` command and associated functionality for [Nanoc](https://nanoc.ws).
4
+
5
+ For details, see the [Deploying Nanoc sites](https://nanoc.ws/doc/deploying/) chapter of the Nanoc documentation.
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nanoc/deploying'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nanoc-core'
4
+ require 'nanoc-cli'
5
+
6
+ module Nanoc
7
+ module Deploying
8
+ end
9
+ end
10
+
11
+ require 'nanoc/deploying/version'
12
+ require 'nanoc/deploying/deployer'
13
+ require 'nanoc/deploying/deployers'
14
+
15
+ require 'nanoc/deploying/command_runners'
16
+
17
+ root = File.dirname(__FILE__)
18
+ deploying_command_path = File.join(root, 'deploying', 'commands', 'deploy.rb')
19
+ command = Cri::Command.load_file(deploying_command_path, infer_name: true)
20
+
21
+ Nanoc::CLI.after_setup do
22
+ Nanoc::CLI.add_command(command)
23
+ Nanoc::CLI::Commands::ShowPlugins.add_plugin_class(Nanoc::Deploying::Deployer, 'Deployers')
24
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ module Nanoc
5
+ module Deploying
6
+ module CommandRunners
7
+ end
8
+ end
9
+ end
10
+
11
+ require_relative 'command_runners/deploy'
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Deploying
5
+ module CommandRunners
6
+ class Deploy < ::Nanoc::CLI::CommandRunner
7
+ def run
8
+ @site = load_site
9
+ Nanoc::Core::Compiler.new_for(@site).run_until_preprocessed
10
+
11
+ if options[:'list-deployers']
12
+ list_deployers
13
+ elsif options[:list]
14
+ list_deploy_configs
15
+ else
16
+ deploy
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def list_deployers
23
+ deployers = Nanoc::Deploying::Deployer.all
24
+ deployer_names = deployers.map(&:identifier).sort
25
+ puts 'Available deployers:'
26
+ deployer_names.each do |name|
27
+ puts " #{name}"
28
+ end
29
+ end
30
+
31
+ def list_deploy_configs
32
+ if deploy_configs.empty?
33
+ puts 'No deployment configurations.'
34
+ else
35
+ puts 'Available deployment configurations:'
36
+ deploy_configs.each_key do |name|
37
+ puts " #{name}"
38
+ end
39
+ end
40
+ end
41
+
42
+ def deploy
43
+ deployer = deployer_for(deploy_config)
44
+
45
+ checks_successful = options[:'no-check'] ? true : check
46
+ return unless checks_successful
47
+
48
+ deployer.run
49
+ end
50
+
51
+ def deploy_config
52
+ if deploy_configs.empty?
53
+ raise Nanoc::Core::TrivialError, 'The site has no deployment configurations.'
54
+ end
55
+
56
+ if arguments.length > 1
57
+ raise Nanoc::Core::TrivialError, "usage: #{command.usage}"
58
+ end
59
+
60
+ target_from_arguments = arguments[0]
61
+ target_from_options = options.fetch(:target, nil)
62
+ if target_from_arguments && target_from_options
63
+ raise Nanoc::Core::TrivialError, 'Only one deployment target can be specified on the command line.'
64
+ end
65
+
66
+ target = target_from_arguments || target_from_options || :default
67
+ deploy_configs.fetch(target.to_sym) do
68
+ raise Nanoc::Core::TrivialError, "The site has no deployment configuration named `#{target}`."
69
+ end
70
+ end
71
+
72
+ def deployer_for(config)
73
+ deployer_class_for_config(config).new(
74
+ @site.config.output_dir,
75
+ config,
76
+ dry_run: options[:'dry-run'],
77
+ )
78
+ end
79
+
80
+ def check
81
+ runner = Nanoc::Checking::Runner.new(@site)
82
+ if runner.any_enabled_checks?
83
+ puts 'Running issue checks…'
84
+ is_success = runner.run_for_deploy
85
+ if is_success
86
+ puts 'No issues found. Deploying!'
87
+ else
88
+ puts 'Issues found, deploy aborted.'
89
+ end
90
+ is_success
91
+ else
92
+ true
93
+ end
94
+ end
95
+
96
+ def deploy_configs
97
+ @site.config.fetch(:deploy, {})
98
+ end
99
+
100
+ def deployer_class_for_config(config)
101
+ name = config.fetch(:kind) do
102
+ $stderr.puts 'Warning: The specified deploy target does not have a kind attribute. Assuming rsync.'
103
+ 'rsync'
104
+ end
105
+
106
+ deployer_class = Nanoc::Deploying::Deployer.named(name.to_sym)
107
+ if deployer_class.nil?
108
+ names = Nanoc::Deploying::Deployer.all.map(&:identifier)
109
+ raise Nanoc::Core::TrivialError, "The specified deploy target has an unrecognised kind “#{name}” (expected one of #{names.join(', ')})."
110
+ end
111
+ deployer_class
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ usage 'deploy [target] [options]'
4
+ summary 'deploy the compiled site'
5
+ description "
6
+ Deploys the compiled site. The compiled site contents in the output directory will be uploaded to the destination, which is specified using the `--target` option.
7
+ "
8
+
9
+ option :t, :target, 'specify the location to deploy to (default: `default`)', argument: :required
10
+ flag :C, :'no-check', 'do not run the issue checks marked for deployment'
11
+ flag :L, :list, 'list available locations to deploy to'
12
+ flag :D, :'list-deployers', 'list available deployers'
13
+ option :n, :'dry-run', 'show what would be deployed'
14
+
15
+ runner Nanoc::Deploying::CommandRunners::Deploy
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Deploying
5
+ # Represents a deployer, an object that allows uploading the compiled site
6
+ # to a specific (remote) location.
7
+ #
8
+ # @abstract Subclass and override {#run} to implement a custom filter.
9
+ #
10
+ # @api private
11
+ class Deployer
12
+ extend DDPlugin::Plugin
13
+
14
+ # @return [String] The path to the directory that contains the files to
15
+ # upload. It should not have a trailing slash.
16
+ attr_reader :source_path
17
+
18
+ # @return [Hash] The deployer configuration
19
+ attr_reader :config
20
+
21
+ # @return [Boolean] true if the deployer should only show what would be
22
+ # deployed instead of doing the actual deployment
23
+ attr_reader :dry_run
24
+ alias dry_run? dry_run
25
+
26
+ # @param [String] source_path The path to the directory that contains the
27
+ # files to upload. It should not have a trailing slash.
28
+ #
29
+ # @return [Hash] config The deployer configuration
30
+ #
31
+ # @param [Boolean] dry_run true if the deployer should
32
+ # only show what would be deployed instead actually deploying
33
+ def initialize(source_path, config, dry_run: false)
34
+ @source_path = source_path
35
+ @config = config
36
+ @dry_run = dry_run
37
+ end
38
+
39
+ # Performs the actual deployment.
40
+ #
41
+ # @abstract
42
+ def run
43
+ raise NotImplementedError.new('Nanoc::Deploying::Deployer subclasses must implement #run')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-command'
4
+
5
+ # @api private
6
+ module Nanoc
7
+ module Deploying
8
+ module Deployers
9
+ end
10
+ end
11
+ end
12
+
13
+ require_relative 'deployers/fog'
14
+ require_relative 'deployers/git'
15
+ require_relative 'deployers/rsync'
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Deploying
5
+ module Deployers
6
+ # A deployer that deploys a site using [fog](https://github.com/fog/fog).
7
+ #
8
+ # @example A deployment configuration with public and staging configurations
9
+ #
10
+ # deploy:
11
+ # public:
12
+ # kind: fog
13
+ # bucket: nanoc-site
14
+ # cdn_id: XXXXXX
15
+ # preprod:
16
+ # kind: fog
17
+ # provider: local
18
+ # local_root: ~/myCloud
19
+ # bucket: nanoc-site
20
+ # staging:
21
+ # kind: fog
22
+ # provider: local
23
+ # local_root: ~/myCloud
24
+ # bucket: nanoc-site-staging
25
+ #
26
+ # @api private
27
+ class Fog < ::Nanoc::Deploying::Deployer
28
+ identifier :fog
29
+
30
+ class FogWrapper
31
+ def initialize(directory, is_dry_run)
32
+ @directory = directory
33
+ @is_dry_run = is_dry_run
34
+ end
35
+
36
+ def upload(source_filename, destination_key)
37
+ log_effectful("uploading #{source_filename} -> #{destination_key}")
38
+
39
+ unless dry_run?
40
+ File.open(source_filename) do |io|
41
+ @directory.files.create(
42
+ key: destination_key,
43
+ body: io,
44
+ public: true,
45
+ )
46
+ end
47
+ end
48
+ end
49
+
50
+ def remove(keys)
51
+ keys.each do |key|
52
+ log_effectful("removing #{key}")
53
+
54
+ unless dry_run?
55
+ @directory.files.get(key).destroy
56
+ end
57
+ end
58
+ end
59
+
60
+ def invalidate(keys, cdn, distribution)
61
+ keys.each_slice(1000) do |keys_slice|
62
+ keys_slice.each do |key|
63
+ log_effectful("invalidating #{key}")
64
+ end
65
+
66
+ unless dry_run?
67
+ cdn.post_invalidation(distribution, keys_slice)
68
+ end
69
+ end
70
+ end
71
+
72
+ def dry_run?
73
+ @is_dry_run
74
+ end
75
+
76
+ def log_effectful(str)
77
+ if @is_dry_run
78
+ puts "[dry run] #{str}"
79
+ else
80
+ puts str
81
+ end
82
+ end
83
+ end
84
+
85
+ # @see Nanoc::Deploying::Deployer#run
86
+ def run
87
+ require 'fog/core'
88
+
89
+ src = File.expand_path(source_path)
90
+ bucket = config[:bucket] || config[:bucket_name]
91
+ path = config[:path]
92
+ cdn_id = config[:cdn_id]
93
+
94
+ if path&.end_with?('/')
95
+ raise "The path `#{path}` is not supposed to have a trailing slash"
96
+ end
97
+
98
+ connection = connect
99
+ directory = get_or_create_bucket(connection, bucket, path)
100
+ wrapper = FogWrapper.new(directory, dry_run?)
101
+
102
+ remote_files = list_remote_files(directory)
103
+ etags = read_etags(remote_files)
104
+
105
+ modified_keys, retained_keys = upload_all(src, path, etags, wrapper)
106
+
107
+ removed_keys = remote_files.map(&:key) - retained_keys - modified_keys
108
+ wrapper.remove(removed_keys)
109
+
110
+ if cdn_id
111
+ cdn = ::Fog::CDN.new(config_for_fog)
112
+ distribution = cdn.get_distribution(cdn_id)
113
+ wrapper.invalidate(modified_keys + removed_keys, cdn, distribution)
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ def config_for_fog
120
+ config.dup.tap do |c|
121
+ c.delete(:bucket)
122
+ c.delete(:bucket_name)
123
+ c.delete(:path)
124
+ c.delete(:cdn_id)
125
+ c.delete(:kind)
126
+ end
127
+ end
128
+
129
+ def connect
130
+ ::Fog::Storage.new(config_for_fog)
131
+ rescue ArgumentError
132
+ require "fog/#{config[:provider]}"
133
+ ::Fog::Storage.new(config_for_fog)
134
+ end
135
+
136
+ def get_or_create_bucket(connection, bucket, path)
137
+ directory =
138
+ begin
139
+ connection.directories.get(bucket, prefix: path)
140
+ rescue ::Excon::Errors::NotFound
141
+ nil
142
+ end
143
+
144
+ if directory
145
+ directory
146
+ elsif dry_run?
147
+ puts '[dry run] creating bucket'
148
+ else
149
+ puts 'creating bucket'
150
+ connection.directories.create(key: bucket, prefix: path)
151
+ end
152
+ end
153
+
154
+ def remote_key_for_local_filename(local_filename, src, path)
155
+ relative_local_filename = local_filename.sub(/\A#{src}\//, '')
156
+
157
+ if path
158
+ File.join(path, relative_local_filename)
159
+ else
160
+ relative_local_filename
161
+ end
162
+ end
163
+
164
+ def list_remote_files(directory)
165
+ if directory
166
+ [].tap do |files|
167
+ directory.files.each { |file| files << file }
168
+ end
169
+ else
170
+ []
171
+ end
172
+ end
173
+
174
+ def list_local_files(src)
175
+ Dir[src + '/**/*'].select { |f| File.file?(f) }
176
+ end
177
+
178
+ def upload_all(src, path, etags, wrapper)
179
+ modified_keys = []
180
+ retained_keys = []
181
+
182
+ local_files = list_local_files(src)
183
+ local_files.each do |file_path|
184
+ key = remote_key_for_local_filename(file_path, src, path)
185
+ if needs_upload?(key, file_path, etags)
186
+ wrapper.upload(file_path, key)
187
+ modified_keys.push(key)
188
+ else
189
+ retained_keys.push(key)
190
+ end
191
+ end
192
+
193
+ [modified_keys, retained_keys]
194
+ end
195
+
196
+ def needs_upload?(key, file_path, etags)
197
+ remote_etag = etags[key]
198
+ return true if remote_etag.nil?
199
+
200
+ local_etag = calc_local_etag(file_path)
201
+ remote_etag != local_etag
202
+ end
203
+
204
+ def read_etags(files)
205
+ case config[:provider]
206
+ when 'aws'
207
+ files.each_with_object({}) do |file, etags|
208
+ etags[file.key] = file.etag
209
+ end
210
+ else
211
+ {}
212
+ end
213
+ end
214
+
215
+ def calc_local_etag(file_path)
216
+ case config[:provider]
217
+ when 'aws'
218
+ Digest::MD5.file(file_path).hexdigest
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Deploying
5
+ module Deployers
6
+ # A deployer that deploys a site using [Git](https://git-scm.com).
7
+ #
8
+ # @example A deployment configuration for GitHub Pages:
9
+ #
10
+ # deploy:
11
+ # default:
12
+ # kind: git
13
+ # remote: git@github.com:myself/myproject.git
14
+ # branch: gh-pages
15
+ # forced: true
16
+ #
17
+ class Git < ::Nanoc::Deploying::Deployer
18
+ identifier :git
19
+
20
+ module Errors
21
+ class Generic < ::Nanoc::Core::Error
22
+ end
23
+
24
+ class OutputDirDoesNotExist < Generic
25
+ def initialize(path)
26
+ super("The directory to deploy, #{path}, does not exist.")
27
+ end
28
+ end
29
+
30
+ class OutputDirIsNotAGitRepo < Generic
31
+ def initialize(path)
32
+ super("The directory to deploy, #{path}, is not a Git repository.")
33
+ end
34
+ end
35
+
36
+ class RemoteDoesNotExist < Generic
37
+ def initialize(remote)
38
+ super("The remote to deploy to, #{remote}, does not exist.")
39
+ end
40
+ end
41
+
42
+ class BranchDoesNotExist < Generic
43
+ def initialize(branch)
44
+ super("The branch to deploy, #{branch}, does not exist.")
45
+ end
46
+ end
47
+ end
48
+
49
+ def run
50
+ unless File.exist?(source_path)
51
+ raise Errors::OutputDirDoesNotExist.new(source_path)
52
+ end
53
+
54
+ remote = config.fetch(:remote, 'origin')
55
+ branch = config.fetch(:branch, 'master')
56
+ forced = config.fetch(:forced, false)
57
+
58
+ puts "Deploying via Git to branch “#{branch}” on remote “#{remote}”…"
59
+
60
+ Dir.chdir(source_path) do
61
+ unless File.exist?('.git')
62
+ raise Errors::OutputDirIsNotAGitRepo.new(source_path)
63
+ end
64
+
65
+ # Verify existence of remote, if remote is not a URL
66
+ if remote_is_name?(remote)
67
+ begin
68
+ run_cmd(%W[git config --get remote.#{remote}.url])
69
+ rescue TTY::Command::ExitError
70
+ raise Errors::RemoteDoesNotExist.new(remote)
71
+ end
72
+ end
73
+
74
+ # If the branch exists then switch to it, otherwise prompt the user to create one.
75
+ begin
76
+ run_cmd_unless_dry(%W[git checkout #{branch}])
77
+ rescue TTY::Command::ExitError
78
+ raise Errors::BranchDoesNotExist.new(branch)
79
+ end
80
+
81
+ return if clean_repo?
82
+
83
+ msg = "Automated commit at #{Time.now.utc} by Nanoc #{Nanoc::VERSION}"
84
+ author = 'Nanoc <>'
85
+ run_cmd_unless_dry(%w[git add -A])
86
+ run_cmd_unless_dry(%W[git commit -a --author #{author} -m #{msg}])
87
+
88
+ if forced
89
+ run_cmd_unless_dry(%W[git push -f #{remote} #{branch}])
90
+ else
91
+ run_cmd_unless_dry(%W[git push #{remote} #{branch}])
92
+ end
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def remote_is_name?(remote)
99
+ remote !~ /:\/\/|@.+:/
100
+ end
101
+
102
+ def run_cmd(cmd, dry_run: false)
103
+ TTY::Command.new(printer: :null).run(*cmd, dry_run: dry_run)
104
+ end
105
+
106
+ def run_cmd_unless_dry(cmd)
107
+ run_cmd(cmd, dry_run: dry_run)
108
+ end
109
+
110
+ def clean_repo?
111
+ TTY::Command.new(printer: :null).run('git status --porcelain').out.empty?
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Deploying
5
+ module Deployers
6
+ # A deployer that deploys a site using rsync.
7
+ #
8
+ # The configuration has should include a `:dst` value, a string containing
9
+ # the destination to where rsync should upload its data. It will likely be
10
+ # in `host:path` format. It should not end with a slash. For example,
11
+ # `"example.com:/var/www/sites/mysite/html"`.
12
+ #
13
+ # @example A deployment configuration with public and staging configurations
14
+ #
15
+ # deploy:
16
+ # public:
17
+ # kind: rsync
18
+ # dst: "ectype:sites/stoneship/public"
19
+ # staging:
20
+ # kind: rsync
21
+ # dst: "ectype:sites/stoneship-staging/public"
22
+ # options: [ "-glpPrtvz" ]
23
+ #
24
+ # @api private
25
+ class Rsync < ::Nanoc::Deploying::Deployer
26
+ identifier :rsync
27
+
28
+ # Default rsync options
29
+ DEFAULT_OPTIONS = [
30
+ '--group',
31
+ '--links',
32
+ '--perms',
33
+ '--partial',
34
+ '--progress',
35
+ '--recursive',
36
+ '--times',
37
+ '--verbose',
38
+ '--compress',
39
+ '--exclude=".hg"',
40
+ '--exclude=".svn"',
41
+ '--exclude=".git"',
42
+ ].freeze
43
+
44
+ # @see Nanoc::Deploying::Deployer#run
45
+ def run
46
+ # Get params
47
+ src = source_path + '/'
48
+ dst = config[:dst]
49
+ options = config[:options] || DEFAULT_OPTIONS
50
+
51
+ # Validate
52
+ raise 'No dst found in deployment configuration' if dst.nil?
53
+ raise 'dst requires no trailing slash' if dst[-1, 1] == '/'
54
+
55
+ # Run
56
+ if dry_run
57
+ warn 'Performing a dry-run; no actions will actually be performed'
58
+ run_shell_cmd(['echo', 'rsync', options, src, dst].flatten)
59
+ else
60
+ run_shell_cmd(['rsync', options, src, dst].flatten)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def run_shell_cmd(cmd)
67
+ TTY::Command.new(printer: :null).run(*cmd)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Deploying
5
+ VERSION = '1.0.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nanoc-deploying
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Denis Defreyne
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-03-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nanoc-checking
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: nanoc-cli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.11'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 4.11.15
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '4.11'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 4.11.15
47
+ - !ruby/object:Gem::Dependency
48
+ name: nanoc-core
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '4.11'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 4.11.15
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '4.11'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 4.11.15
67
+ description: Provides deploying functionality for Nanoc
68
+ email: denis+rubygems@denis.ws
69
+ executables: []
70
+ extensions: []
71
+ extra_rdoc_files: []
72
+ files:
73
+ - NEWS.md
74
+ - README.md
75
+ - lib/nanoc-deploying.rb
76
+ - lib/nanoc/deploying.rb
77
+ - lib/nanoc/deploying/command_runners.rb
78
+ - lib/nanoc/deploying/command_runners/deploy.rb
79
+ - lib/nanoc/deploying/commands/deploy.rb
80
+ - lib/nanoc/deploying/deployer.rb
81
+ - lib/nanoc/deploying/deployers.rb
82
+ - lib/nanoc/deploying/deployers/fog.rb
83
+ - lib/nanoc/deploying/deployers/git.rb
84
+ - lib/nanoc/deploying/deployers/rsync.rb
85
+ - lib/nanoc/deploying/version.rb
86
+ homepage: https://nanoc.ws/
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '2.4'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.1.2
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Deploying support for Nanoc
109
+ test_files: []