nanoc-deploying 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|