nanoc 4.11.13 → 4.11.18
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 +4 -4
- data/NEWS.md +790 -744
- data/lib/nanoc.rb +4 -2
- data/lib/nanoc/data_sources/filesystem.rb +12 -6
- data/lib/nanoc/extra.rb +1 -0
- data/lib/nanoc/extra/core_ext.rb +0 -1
- data/lib/nanoc/extra/link_collector.rb +1 -1
- data/lib/nanoc/extra/srcset_parser.rb +79 -0
- data/lib/nanoc/filters/colorize_syntax/colorizers.rb +1 -1
- data/lib/nanoc/filters/erb.rb +1 -5
- data/lib/nanoc/filters/relativize_paths.rb +62 -10
- data/lib/nanoc/filters/sass/functions.rb +1 -1
- data/lib/nanoc/helpers/rendering.rb +5 -4
- data/lib/nanoc/orig_cli.rb +0 -5
- data/lib/nanoc/rule_dsl.rb +1 -0
- data/lib/nanoc/rule_dsl/action_provider.rb +1 -1
- data/lib/nanoc/rule_dsl/compiler_dsl.rb +1 -1
- data/lib/nanoc/rule_dsl/errors.rb +25 -0
- data/lib/nanoc/rule_dsl/rules_loader.rb +1 -1
- data/lib/nanoc/version.rb +1 -1
- metadata +37 -33
- data/lib/nanoc/base.rb +0 -13
- data/lib/nanoc/base/changes_stream.rb +0 -53
- data/lib/nanoc/base/errors.rb +0 -65
- data/lib/nanoc/checking.rb +0 -14
- data/lib/nanoc/checking/check.rb +0 -93
- data/lib/nanoc/checking/checks.rb +0 -14
- data/lib/nanoc/checking/checks/css.rb +0 -16
- data/lib/nanoc/checking/checks/external_links.rb +0 -151
- data/lib/nanoc/checking/checks/html.rb +0 -16
- data/lib/nanoc/checking/checks/internal_links.rb +0 -95
- data/lib/nanoc/checking/checks/mixed_content.rb +0 -37
- data/lib/nanoc/checking/checks/stale.rb +0 -41
- data/lib/nanoc/checking/checks/w3c_validator.rb +0 -31
- data/lib/nanoc/checking/dsl.rb +0 -27
- data/lib/nanoc/checking/issue.rb +0 -16
- data/lib/nanoc/checking/loader.rb +0 -50
- data/lib/nanoc/checking/runner.rb +0 -136
- data/lib/nanoc/deploying.rb +0 -10
- data/lib/nanoc/deploying/deployer.rb +0 -45
- data/lib/nanoc/deploying/deployers.rb +0 -11
- data/lib/nanoc/deploying/deployers/fog.rb +0 -220
- data/lib/nanoc/deploying/deployers/git.rb +0 -112
- data/lib/nanoc/deploying/deployers/rsync.rb +0 -68
- data/lib/nanoc/extra/core_ext/pathname.rb +0 -27
- data/lib/nanoc/orig_cli/commands/check.rb +0 -43
- data/lib/nanoc/orig_cli/commands/deploy.rb +0 -126
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Checking::Checks
|
4
|
-
# A check that verifies HTML files do not reference external resources with
|
5
|
-
# URLs that would trigger "mixed content" warnings.
|
6
|
-
#
|
7
|
-
# @api private
|
8
|
-
class MixedContent < ::Nanoc::Checking::Check
|
9
|
-
identifier :mixed_content
|
10
|
-
|
11
|
-
PROTOCOL_PATTERN = /^(\w+):\/\//.freeze
|
12
|
-
|
13
|
-
def run
|
14
|
-
filenames = output_html_filenames
|
15
|
-
resource_uris_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames).filenames_per_resource_uri
|
16
|
-
|
17
|
-
resource_uris_with_filenames.each_pair do |uri, fns|
|
18
|
-
next unless guaranteed_insecure?(uri)
|
19
|
-
|
20
|
-
fns.each do |filename|
|
21
|
-
add_issue(
|
22
|
-
"mixed content include: #{uri}",
|
23
|
-
subject: filename,
|
24
|
-
)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def guaranteed_insecure?(href)
|
32
|
-
protocol = PROTOCOL_PATTERN.match(href)
|
33
|
-
|
34
|
-
protocol && protocol[1].casecmp('http').zero?
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Checking::Checks
|
4
|
-
# @api private
|
5
|
-
class Stale < ::Nanoc::Checking::Check
|
6
|
-
identifier :stale
|
7
|
-
|
8
|
-
def run
|
9
|
-
output_filenames.each do |f|
|
10
|
-
next if pruner.filename_excluded?(f)
|
11
|
-
next if item_rep_paths.include?(f)
|
12
|
-
|
13
|
-
add_issue(
|
14
|
-
'file without matching item',
|
15
|
-
subject: f,
|
16
|
-
)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
protected
|
21
|
-
|
22
|
-
def item_rep_paths
|
23
|
-
@item_rep_paths ||=
|
24
|
-
Set.new(
|
25
|
-
@items
|
26
|
-
.flat_map(&:reps)
|
27
|
-
.map(&:_unwrap)
|
28
|
-
.flat_map(&:raw_paths)
|
29
|
-
.flat_map(&:values)
|
30
|
-
.flatten,
|
31
|
-
)
|
32
|
-
end
|
33
|
-
|
34
|
-
def pruner
|
35
|
-
exclude_config = @config.fetch(:prune, {}).fetch(:exclude, [])
|
36
|
-
# FIXME: specifying reps this way is icky
|
37
|
-
reps = Nanoc::Core::ItemRepRepo.new
|
38
|
-
@pruner ||= Nanoc::Core::Pruner.new(@config._unwrap, reps, exclude: exclude_config)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ::Nanoc::Checking::Checks
|
4
|
-
# @api private
|
5
|
-
class W3CValidator < ::Nanoc::Checking::Check
|
6
|
-
def run
|
7
|
-
require 'w3c_validators'
|
8
|
-
require 'resolv-replace'
|
9
|
-
|
10
|
-
Dir[@config.output_dir + '/**/*.' + extension].each do |filename|
|
11
|
-
results = validator_class.new.validate_file(filename)
|
12
|
-
lines = File.readlines(filename)
|
13
|
-
results.errors.each do |e|
|
14
|
-
line_num = e.line.to_i - 1
|
15
|
-
line = lines[line_num]
|
16
|
-
message = e.message.gsub(%r{\s+}, ' ').strip.sub(/\s+:$/, '')
|
17
|
-
desc = "line #{line_num + 1}: #{message}: #{line}"
|
18
|
-
add_issue(desc, subject: filename)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def extension
|
24
|
-
raise NotImplementedError
|
25
|
-
end
|
26
|
-
|
27
|
-
def validator_class
|
28
|
-
raise NotImplementedError
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
data/lib/nanoc/checking/dsl.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Checking
|
4
|
-
# @api private
|
5
|
-
class DSL
|
6
|
-
def self.from_file(filename, enabled_checks:)
|
7
|
-
dsl = new(enabled_checks: enabled_checks)
|
8
|
-
absolute_filename = File.expand_path(filename)
|
9
|
-
dsl.instance_eval(File.read(filename), absolute_filename)
|
10
|
-
dsl
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(enabled_checks:)
|
14
|
-
@enabled_checks = enabled_checks
|
15
|
-
end
|
16
|
-
|
17
|
-
def check(identifier, &block)
|
18
|
-
klass = Class.new(::Nanoc::Checking::Check)
|
19
|
-
klass.send(:define_method, :run, &block)
|
20
|
-
klass.send(:identifier, identifier)
|
21
|
-
end
|
22
|
-
|
23
|
-
def deploy_check(*identifiers)
|
24
|
-
identifiers.each { |i| @enabled_checks << i }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/nanoc/checking/issue.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Checking
|
4
|
-
# @api private
|
5
|
-
class Issue
|
6
|
-
attr_reader :description
|
7
|
-
attr_reader :subject
|
8
|
-
attr_reader :check_class
|
9
|
-
|
10
|
-
def initialize(desc, subject, check_class)
|
11
|
-
@description = desc
|
12
|
-
@subject = subject
|
13
|
-
@check_class = check_class
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Checking
|
4
|
-
# @api private
|
5
|
-
class Loader
|
6
|
-
CHECKS_FILENAMES = ['Checks', 'Checks.rb', 'checks', 'checks.rb'].freeze
|
7
|
-
|
8
|
-
def initialize(config:)
|
9
|
-
@config = config
|
10
|
-
end
|
11
|
-
|
12
|
-
def run
|
13
|
-
dsl
|
14
|
-
end
|
15
|
-
|
16
|
-
def enabled_checks
|
17
|
-
(enabled_checks_from_dsl + enabled_checks_from_config).uniq
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def dsl_present?
|
23
|
-
checks_filename && File.file?(checks_filename)
|
24
|
-
end
|
25
|
-
|
26
|
-
def enabled_checks_from_dsl
|
27
|
-
dsl
|
28
|
-
@enabled_checks_from_dsl
|
29
|
-
end
|
30
|
-
|
31
|
-
def enabled_checks_from_config
|
32
|
-
@config.fetch(:checking, {}).fetch(:enabled_checks, []).map(&:to_sym)
|
33
|
-
end
|
34
|
-
|
35
|
-
def dsl
|
36
|
-
@enabled_checks_from_dsl ||= []
|
37
|
-
|
38
|
-
@dsl ||=
|
39
|
-
if dsl_present?
|
40
|
-
Nanoc::Checking::DSL.from_file(checks_filename, enabled_checks: @enabled_checks_from_dsl)
|
41
|
-
else
|
42
|
-
Nanoc::Checking::DSL.new(enabled_checks: @enabled_checks_from_dsl)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def checks_filename
|
47
|
-
@_checks_filename ||= CHECKS_FILENAMES.find { |f| File.file?(f) }
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,136 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Checking
|
4
|
-
# Runner is reponsible for running issue checks.
|
5
|
-
#
|
6
|
-
# @api private
|
7
|
-
class Runner
|
8
|
-
# @param [Nanoc::Core::Site] site The Nanoc site this runner is for
|
9
|
-
def initialize(site)
|
10
|
-
@site = site
|
11
|
-
end
|
12
|
-
|
13
|
-
def any_enabled_checks?
|
14
|
-
enabled_checks.any?
|
15
|
-
end
|
16
|
-
|
17
|
-
# Lists all available checks on stdout.
|
18
|
-
#
|
19
|
-
# @return [void]
|
20
|
-
def list_checks
|
21
|
-
load_all
|
22
|
-
|
23
|
-
puts 'Available checks:'
|
24
|
-
puts
|
25
|
-
puts all_check_classes.map { |i| ' ' + i.identifier.to_s }.sort.join("\n")
|
26
|
-
end
|
27
|
-
|
28
|
-
# Runs all checks.
|
29
|
-
#
|
30
|
-
# @return [Boolean] true if successful, false otherwise
|
31
|
-
def run_all
|
32
|
-
load_all
|
33
|
-
run_check_classes(all_check_classes)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Runs the checks marked for deployment.
|
37
|
-
#
|
38
|
-
# @return [Boolean] true if successful, false otherwise
|
39
|
-
def run_for_deploy
|
40
|
-
# TODO: rename to #run_enabled
|
41
|
-
load_all
|
42
|
-
run_check_classes(check_classes_named(enabled_checks))
|
43
|
-
end
|
44
|
-
|
45
|
-
# Runs the checks with the given names.
|
46
|
-
#
|
47
|
-
# @param [Array<Symbol>] check_class_names The names of the checks
|
48
|
-
#
|
49
|
-
# @return [Boolean] true if successful, false otherwise
|
50
|
-
def run_specific(check_class_names)
|
51
|
-
load_all
|
52
|
-
run_check_classes(check_classes_named(check_class_names))
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def loader
|
58
|
-
@loader ||= Nanoc::Checking::Loader.new(config: @site.config)
|
59
|
-
end
|
60
|
-
|
61
|
-
def load_all
|
62
|
-
loader.run
|
63
|
-
end
|
64
|
-
|
65
|
-
def enabled_checks
|
66
|
-
loader.enabled_checks
|
67
|
-
end
|
68
|
-
|
69
|
-
def run_check_classes(classes)
|
70
|
-
issues = run_checks(classes)
|
71
|
-
print_issues(issues)
|
72
|
-
issues.empty? ? true : false
|
73
|
-
end
|
74
|
-
|
75
|
-
def all_check_classes
|
76
|
-
Nanoc::Checking::Check.all
|
77
|
-
end
|
78
|
-
|
79
|
-
def check_classes_named(names)
|
80
|
-
names.map do |name|
|
81
|
-
name = name.to_s.tr('-', '_').to_sym
|
82
|
-
klass = Nanoc::Checking::Check.named(name)
|
83
|
-
raise Nanoc::Core::TrivialError, "Unknown check: #{name}" if klass.nil?
|
84
|
-
|
85
|
-
klass
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def run_checks(classes)
|
90
|
-
return [] if classes.empty?
|
91
|
-
|
92
|
-
# TODO: remove me
|
93
|
-
Nanoc::Core::Compiler.new_for(@site).run_until_reps_built
|
94
|
-
|
95
|
-
checks = []
|
96
|
-
issues = Set.new
|
97
|
-
length = classes.map { |c| c.identifier.to_s.length }.max + 18
|
98
|
-
classes.each do |klass|
|
99
|
-
print format(" %-#{length}s", "Running check #{klass.identifier}… ")
|
100
|
-
|
101
|
-
check = klass.create(@site)
|
102
|
-
check.run
|
103
|
-
|
104
|
-
checks << check
|
105
|
-
issues.merge(check.issues)
|
106
|
-
|
107
|
-
# TODO: report progress
|
108
|
-
|
109
|
-
puts check.issues.empty? ? 'ok'.green : 'error'.red
|
110
|
-
end
|
111
|
-
issues
|
112
|
-
end
|
113
|
-
|
114
|
-
def subject_to_s(str)
|
115
|
-
str || '(global)'
|
116
|
-
end
|
117
|
-
|
118
|
-
def print_issues(issues)
|
119
|
-
require 'colored'
|
120
|
-
|
121
|
-
return if issues.empty?
|
122
|
-
|
123
|
-
puts 'Issues found!'
|
124
|
-
issues.group_by(&:subject).to_a.sort_by { |s| subject_to_s(s.first) }.each do |pair|
|
125
|
-
subject = pair.first
|
126
|
-
issues = pair.last
|
127
|
-
next if issues.empty?
|
128
|
-
|
129
|
-
puts " #{subject_to_s(subject)}:"
|
130
|
-
issues.each do |i|
|
131
|
-
puts " [ #{'ERROR'.red} ] #{i.check_class.identifier} - #{i.description}"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
data/lib/nanoc/deploying.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Deploying
|
4
|
-
# Represents a deployer, an object that allows uploading the compiled site
|
5
|
-
# to a specific (remote) location.
|
6
|
-
#
|
7
|
-
# @abstract Subclass and override {#run} to implement a custom filter.
|
8
|
-
#
|
9
|
-
# @api private
|
10
|
-
class Deployer
|
11
|
-
extend DDPlugin::Plugin
|
12
|
-
|
13
|
-
# @return [String] The path to the directory that contains the files to
|
14
|
-
# upload. It should not have a trailing slash.
|
15
|
-
attr_reader :source_path
|
16
|
-
|
17
|
-
# @return [Hash] The deployer configuration
|
18
|
-
attr_reader :config
|
19
|
-
|
20
|
-
# @return [Boolean] true if the deployer should only show what would be
|
21
|
-
# deployed instead of doing the actual deployment
|
22
|
-
attr_reader :dry_run
|
23
|
-
alias dry_run? dry_run
|
24
|
-
|
25
|
-
# @param [String] source_path The path to the directory that contains the
|
26
|
-
# files to upload. It should not have a trailing slash.
|
27
|
-
#
|
28
|
-
# @return [Hash] config The deployer configuration
|
29
|
-
#
|
30
|
-
# @param [Boolean] dry_run true if the deployer should
|
31
|
-
# only show what would be deployed instead actually deploying
|
32
|
-
def initialize(source_path, config, dry_run: false)
|
33
|
-
@source_path = source_path
|
34
|
-
@config = config
|
35
|
-
@dry_run = dry_run
|
36
|
-
end
|
37
|
-
|
38
|
-
# Performs the actual deployment.
|
39
|
-
#
|
40
|
-
# @abstract
|
41
|
-
def run
|
42
|
-
raise NotImplementedError.new('Nanoc::Deploying::Deployer subclasses must implement #run')
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,220 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc::Deploying::Deployers
|
4
|
-
# A deployer that deploys a site using [fog](https://github.com/fog/fog).
|
5
|
-
#
|
6
|
-
# @example A deployment configuration with public and staging configurations
|
7
|
-
#
|
8
|
-
# deploy:
|
9
|
-
# public:
|
10
|
-
# kind: fog
|
11
|
-
# bucket: nanoc-site
|
12
|
-
# cdn_id: XXXXXX
|
13
|
-
# preprod:
|
14
|
-
# kind: fog
|
15
|
-
# provider: local
|
16
|
-
# local_root: ~/myCloud
|
17
|
-
# bucket: nanoc-site
|
18
|
-
# staging:
|
19
|
-
# kind: fog
|
20
|
-
# provider: local
|
21
|
-
# local_root: ~/myCloud
|
22
|
-
# bucket: nanoc-site-staging
|
23
|
-
#
|
24
|
-
# @api private
|
25
|
-
class Fog < ::Nanoc::Deploying::Deployer
|
26
|
-
identifier :fog
|
27
|
-
|
28
|
-
class FogWrapper
|
29
|
-
def initialize(directory, is_dry_run)
|
30
|
-
@directory = directory
|
31
|
-
@is_dry_run = is_dry_run
|
32
|
-
end
|
33
|
-
|
34
|
-
def upload(source_filename, destination_key)
|
35
|
-
log_effectful("uploading #{source_filename} -> #{destination_key}")
|
36
|
-
|
37
|
-
unless dry_run?
|
38
|
-
File.open(source_filename) do |io|
|
39
|
-
@directory.files.create(
|
40
|
-
key: destination_key,
|
41
|
-
body: io,
|
42
|
-
public: true,
|
43
|
-
)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def remove(keys)
|
49
|
-
keys.each do |key|
|
50
|
-
log_effectful("removing #{key}")
|
51
|
-
|
52
|
-
unless dry_run?
|
53
|
-
@directory.files.get(key).destroy
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def invalidate(keys, cdn, distribution)
|
59
|
-
keys.each_slice(1000) do |keys_slice|
|
60
|
-
keys_slice.each do |key|
|
61
|
-
log_effectful("invalidating #{key}")
|
62
|
-
end
|
63
|
-
|
64
|
-
unless dry_run?
|
65
|
-
cdn.post_invalidation(distribution, keys_slice)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def dry_run?
|
71
|
-
@is_dry_run
|
72
|
-
end
|
73
|
-
|
74
|
-
def log_effectful(str)
|
75
|
-
if @is_dry_run
|
76
|
-
puts "[dry run] #{str}"
|
77
|
-
else
|
78
|
-
puts str
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# @see Nanoc::Deploying::Deployer#run
|
84
|
-
def run
|
85
|
-
require 'fog/core'
|
86
|
-
|
87
|
-
src = File.expand_path(source_path)
|
88
|
-
bucket = config[:bucket] || config[:bucket_name]
|
89
|
-
path = config[:path]
|
90
|
-
cdn_id = config[:cdn_id]
|
91
|
-
|
92
|
-
if path&.end_with?('/')
|
93
|
-
raise "The path `#{path}` is not supposed to have a trailing slash"
|
94
|
-
end
|
95
|
-
|
96
|
-
connection = connect
|
97
|
-
directory = get_or_create_bucket(connection, bucket, path)
|
98
|
-
wrapper = FogWrapper.new(directory, dry_run?)
|
99
|
-
|
100
|
-
remote_files = list_remote_files(directory)
|
101
|
-
etags = read_etags(remote_files)
|
102
|
-
|
103
|
-
modified_keys, retained_keys = upload_all(src, path, etags, wrapper)
|
104
|
-
|
105
|
-
removed_keys = remote_files.map(&:key) - retained_keys - modified_keys
|
106
|
-
wrapper.remove(removed_keys)
|
107
|
-
|
108
|
-
if cdn_id
|
109
|
-
cdn = ::Fog::CDN.new(config_for_fog)
|
110
|
-
distribution = cdn.get_distribution(cdn_id)
|
111
|
-
wrapper.invalidate(modified_keys + removed_keys, cdn, distribution)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
def config_for_fog
|
118
|
-
config.dup.tap do |c|
|
119
|
-
c.delete(:bucket)
|
120
|
-
c.delete(:bucket_name)
|
121
|
-
c.delete(:path)
|
122
|
-
c.delete(:cdn_id)
|
123
|
-
c.delete(:kind)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def connect
|
128
|
-
::Fog::Storage.new(config_for_fog)
|
129
|
-
rescue ArgumentError
|
130
|
-
require "fog/#{config[:provider]}"
|
131
|
-
::Fog::Storage.new(config_for_fog)
|
132
|
-
end
|
133
|
-
|
134
|
-
def get_or_create_bucket(connection, bucket, path)
|
135
|
-
directory =
|
136
|
-
begin
|
137
|
-
connection.directories.get(bucket, prefix: path)
|
138
|
-
rescue ::Excon::Errors::NotFound
|
139
|
-
nil
|
140
|
-
end
|
141
|
-
|
142
|
-
if directory
|
143
|
-
directory
|
144
|
-
elsif dry_run?
|
145
|
-
puts '[dry run] creating bucket'
|
146
|
-
else
|
147
|
-
puts 'creating bucket'
|
148
|
-
connection.directories.create(key: bucket, prefix: path)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
def remote_key_for_local_filename(local_filename, src, path)
|
153
|
-
relative_local_filename = local_filename.sub(/\A#{src}\//, '')
|
154
|
-
|
155
|
-
if path
|
156
|
-
File.join(path, relative_local_filename)
|
157
|
-
else
|
158
|
-
relative_local_filename
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def list_remote_files(directory)
|
163
|
-
if directory
|
164
|
-
[].tap do |files|
|
165
|
-
directory.files.each { |file| files << file }
|
166
|
-
end
|
167
|
-
else
|
168
|
-
[]
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def list_local_files(src)
|
173
|
-
Dir[src + '/**/*'].select { |f| File.file?(f) }
|
174
|
-
end
|
175
|
-
|
176
|
-
def upload_all(src, path, etags, wrapper)
|
177
|
-
modified_keys = []
|
178
|
-
retained_keys = []
|
179
|
-
|
180
|
-
local_files = list_local_files(src)
|
181
|
-
local_files.each do |file_path|
|
182
|
-
key = remote_key_for_local_filename(file_path, src, path)
|
183
|
-
if needs_upload?(key, file_path, etags)
|
184
|
-
wrapper.upload(file_path, key)
|
185
|
-
modified_keys.push(key)
|
186
|
-
else
|
187
|
-
retained_keys.push(key)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
[modified_keys, retained_keys]
|
192
|
-
end
|
193
|
-
|
194
|
-
def needs_upload?(key, file_path, etags)
|
195
|
-
remote_etag = etags[key]
|
196
|
-
return true if remote_etag.nil?
|
197
|
-
|
198
|
-
local_etag = calc_local_etag(file_path)
|
199
|
-
remote_etag != local_etag
|
200
|
-
end
|
201
|
-
|
202
|
-
def read_etags(files)
|
203
|
-
case config[:provider]
|
204
|
-
when 'aws'
|
205
|
-
files.each_with_object({}) do |file, etags|
|
206
|
-
etags[file.key] = file.etag
|
207
|
-
end
|
208
|
-
else
|
209
|
-
{}
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def calc_local_etag(file_path)
|
214
|
-
case config[:provider]
|
215
|
-
when 'aws'
|
216
|
-
Digest::MD5.file(file_path).hexdigest
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|