nanoc-deploying 1.0.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 +7 -0
- data/NEWS.md +5 -0
- data/README.md +5 -0
- data/lib/nanoc-deploying.rb +3 -0
- data/lib/nanoc/deploying.rb +24 -0
- data/lib/nanoc/deploying/command_runners.rb +11 -0
- data/lib/nanoc/deploying/command_runners/deploy.rb +116 -0
- data/lib/nanoc/deploying/commands/deploy.rb +15 -0
- data/lib/nanoc/deploying/deployer.rb +47 -0
- data/lib/nanoc/deploying/deployers.rb +15 -0
- data/lib/nanoc/deploying/deployers/fog.rb +224 -0
- data/lib/nanoc/deploying/deployers/git.rb +116 -0
- data/lib/nanoc/deploying/deployers/rsync.rb +72 -0
- data/lib/nanoc/deploying/version.rb +7 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -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
data/README.md
ADDED
@@ -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,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
|
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: []
|