Module: Middleman::S3Sync

Extended by:
CachingPolicy, Status
Defined in:
lib/middleman/s3_sync.rb,
lib/middleman/s3_sync/status.rb,
lib/middleman/s3_sync/options.rb,
lib/middleman/s3_sync/version.rb,
lib/middleman/s3_sync/resource.rb,
lib/middleman/s3_sync/cloudfront.rb,
lib/middleman/s3_sync/caching_policy.rb,
lib/middleman/s3_sync/indifferent_hash.rb

Defined Under Namespace

Modules: CachingPolicy, CloudFront, Status Classes: BrowserCachePolicy, IndifferentHash, Options, Resource

Constant Summary collapse

THREADS_COUNT =
8
VERSION =
"4.8.0"
@@bucket_lock =
Mutex.new
@@bucket_files_lock =
Mutex.new
@@invalidation_paths_lock =
Mutex.new

Class Attribute Summary collapse

Class Method Summary collapse

Methods included from CachingPolicy

add_caching_policy, caching_policies, caching_policy_for, default_caching_policy

Methods included from Status

say_status

Class Attribute Details

.appObject

Returns the value of attribute app.



29
30
31
# File 'lib/middleman/s3_sync.rb', line 29

def app
  @app
end

.invalidation_pathsObject (readonly)

Track paths that were changed during sync for CloudFront invalidation Using a Set for O(1) lookups



35
36
37
# File 'lib/middleman/s3_sync.rb', line 35

def invalidation_paths
  @invalidation_paths
end

.mm_resourcesObject

Returns the value of attribute mm_resources.



28
29
30
# File 'lib/middleman/s3_sync.rb', line 28

def mm_resources
  @mm_resources
end

.s3_sync_optionsObject

Returns the value of attribute s3_sync_options.



27
28
29
# File 'lib/middleman/s3_sync.rb', line 27

def s3_sync_options
  @s3_sync_options
end

Class Method Details

.add_invalidation_path(path) ⇒ Object



92
93
94
95
96
97
98
99
# File 'lib/middleman/s3_sync.rb', line 92

def add_invalidation_path(path)
  @@invalidation_paths_lock.synchronize do
    @invalidation_paths ||= Set.new
    # Normalize path for CloudFront (ensure it starts with /)
    normalized_path = path.start_with?('/') ? path : "/#{path}"
    @invalidation_paths.add(normalized_path)
  end
end

.add_local_resource(mm_resource) ⇒ Object



88
89
90
# File 'lib/middleman/s3_sync.rb', line 88

def add_local_resource(mm_resource)
  s3_sync_resources[mm_resource.destination_path] = S3Sync::Resource.new(mm_resource, remote_resource_for_path(mm_resource.destination_path)).tap(&:status)
end

.bucketObject



78
79
80
81
82
83
84
85
86
# File 'lib/middleman/s3_sync.rb', line 78

def bucket
  @@bucket_lock.synchronize do
    @bucket ||= begin
                  bucket = s3_resource.bucket(s3_sync_options.bucket)
                  raise "Bucket #{s3_sync_options.bucket} doesn't exist!" unless bucket.exists?
                  bucket
                end
  end
end

.content_typesObject



109
110
111
# File 'lib/middleman/s3_sync.rb', line 109

def content_types
  @content_types || {}
end

.remote_only_pathsObject



101
102
103
# File 'lib/middleman/s3_sync.rb', line 101

def remote_only_paths
  paths - s3_sync_resources.keys
end

.run_after_s3_sync_callbackObject

Run the after_s3_sync callback if configured



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/middleman/s3_sync.rb', line 114

def run_after_s3_sync_callback
  callback = s3_sync_options.after_s3_sync
  return unless callback

  say_status 'callback', 'Running after_s3_sync callback...'
  
  # Build sync results hash
  results = {
    created: files_to_create.size,
    updated: files_to_update.size,
    deleted: files_to_delete.size,
    invalidation_paths: @invalidation_paths.to_a
  }

  begin
    if callback.respond_to?(:call)
      # Lambda/Proc callback - check arity to decide whether to pass results
      if callback.arity == 0 || callback.arity == -1 && callback.parameters.empty?
        callback.call
      else
        callback.call(results)
      end
    elsif callback.is_a?(Symbol) && @app.respond_to?(callback)
      # Symbol method name - call on app
      if @app.method(callback).arity == 0
        @app.send(callback)
      else
        @app.send(callback, results)
      end
    end
    
    say_status 'callback', 'after_s3_sync completed successfully'
  rescue => e
    say_status 'error', "after_s3_sync callback failed: #{e.message}"
  end
end

.syncObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/middleman/s3_sync.rb', line 37

def sync()
  @app ||= ::Middleman::Application.new
  @invalidation_paths = Set.new

  # Ensure sitemap is fully populated before syncing
  # This catches resources from extensions activated in :build mode
  if @app.respond_to?(:sitemap) && @app.sitemap.respond_to?(:ensure_resource_list_updated!)
    @app.sitemap.ensure_resource_list_updated!
  end

  say_status "Let's see if there's work to be done..."
  unless work_to_be_done?
    say_status "All S3 files are up to date."
    
    # Still run CloudFront invalidation if requested for all paths
    if s3_sync_options.cloudfront_invalidate && s3_sync_options.cloudfront_invalidate_all
      CloudFront.invalidate([], s3_sync_options)
    end
    return
  end

  say_status "Ready to apply updates to #{s3_sync_options.bucket}."

  update_bucket_versioning

  update_bucket_website

  ignore_resources
  create_resources
  update_resources
  delete_resources
  
  # Invalidate CloudFront cache if requested
  if s3_sync_options.cloudfront_invalidate
    CloudFront.invalidate(@invalidation_paths.to_a, s3_sync_options)
  end

  # Run after_s3_sync callback if provided
  run_after_s3_sync_callback
end