Class: S3AssetDeploy::Manager

Inherits:
Object
  • Object
show all
Defined in:
lib/s3_asset_deploy/manager.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bucket_name, s3_client_options: {}, logger: nil, local_asset_collector: nil, upload_options: {}, remove_fingerprint: nil) ⇒ Manager

Returns a new instance of Manager.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/s3_asset_deploy/manager.rb', line 14

def initialize(bucket_name, s3_client_options: {}, logger: nil, local_asset_collector: nil, upload_options: {}, remove_fingerprint: nil)
  @bucket_name = bucket_name.to_s
  @logger = logger || Logger.new(STDOUT)

  @s3_client_options = {
    region: "us-east-1",
    logger: @logger
  }.merge(s3_client_options)
  @upload_options = upload_options

  @local_asset_collector = local_asset_collector || S3AssetDeploy::RailsLocalAssetCollector.new(remove_fingerprint: remove_fingerprint)
  @remote_asset_collector = S3AssetDeploy::RemoteAssetCollector.new(
    bucket_name,
    s3_client_options: @s3_client_options,
    remove_fingerprint: remove_fingerprint
  )
end

Instance Attribute Details

#bucket_nameObject (readonly)

Returns the value of attribute bucket_name.



12
13
14
# File 'lib/s3_asset_deploy/manager.rb', line 12

def bucket_name
  @bucket_name
end

#local_asset_collectorObject (readonly)

Returns the value of attribute local_asset_collector.



12
13
14
# File 'lib/s3_asset_deploy/manager.rb', line 12

def local_asset_collector
  @local_asset_collector
end

#loggerObject (readonly)

Returns the value of attribute logger.



12
13
14
# File 'lib/s3_asset_deploy/manager.rb', line 12

def logger
  @logger
end

#remote_asset_collectorObject (readonly)

Returns the value of attribute remote_asset_collector.



12
13
14
# File 'lib/s3_asset_deploy/manager.rb', line 12

def remote_asset_collector
  @remote_asset_collector
end

Instance Method Details

#clean(version_limit: 2, version_ttl: 3600, removed_ttl: 172800, dry_run: false) ⇒ Object

Cleanup old assets on S3. By default it will keep the latest version, 2 older versions (version_limit) and any created within the past hour (version_ttl). When assets are removed completely, they are tagged with a removed_at timestamp and eventually deleted based on the removed_ttl.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
# File 'lib/s3_asset_deploy/manager.rb', line 72

def clean(version_limit: 2, version_ttl: 3600, removed_ttl: 172800, dry_run: false)
  verify_no_duplicate_assets!

  version_ttl = version_ttl.to_i
  removed_ttl = removed_ttl.to_i

  log "Cleaning assets from #{bucket_name} S3 bucket. Dry run: #{dry_run}."
  s3_keys_to_delete = []

  unless local_assets_to_upload.empty?
    log "WARNING: Please upload latest asset versions to remote host before cleaning."
    return s3_keys_to_delete
  end

  removal_manifest.load
  local_asset_map = local_asset_collector.asset_map

  remote_asset_collector.grouped_assets.each do |original_path, versions|
    current_asset = local_asset_map[original_path]

    # Remove current asset version from the list
    versions_to_delete = versions.reject do |version|
      version.path == current_asset.path if current_asset
    end

    # Sort remaining versions from newest to oldest
    versions_to_delete = versions_to_delete.sort_by(&:last_modified).reverse

    # If the asset has been completely removed from our set of locally compiled assets
    # then use removed_at timestamp from manifest and removed_ttl to determine if it
    # should be deleted from remote host.
    # Otherwise, use version_ttl and version_limit to dermine whether version should be kept.
    versions_to_delete = versions_to_delete.each_with_index.drop_while do |version, index|
      if !current_asset
        if (removed_at = removal_manifest[version.path])
          removed_at = Time.parse(removed_at)
          removed_age = Time.now.utc - removed_at
          log "Determining how long ago #{version.path} was removed - removed on #{removed_at} (#{removed_age} seconds ago)."
          drop = removed_age < removed_ttl
          log "Marking removed asset #{version.path} for deletion." unless drop
          removal_manifest.delete(version.path) unless drop || dry_run
          drop
        else
          log "Adding #{version.path} to removal manifest."
          removed_at = Time.now.utc
          removal_manifest[version.path] = removed_at.iso8601 unless dry_run
          true
        end
      else
        # Keep if under age or within the version_limit
        version_age = [0, Time.now - version.last_modified].max
        drop = version_age < version_ttl || index < version_limit
        log "Marking #{version.path} for deletion. Version age: #{version_age}. Version: #{index + 1}." unless drop
        drop
      end
    end.map(&:first)

    s3_keys_to_delete += versions_to_delete.map(&:path)
  end

  unless dry_run
    delete_objects(s3_keys_to_delete)
    removal_manifest.save
  end

  remote_asset_collector.clear_cache
  s3_keys_to_delete
end

#deploy(version_limit: 2, version_ttl: 3600, removed_ttl: 172800, clean: true, dry_run: false) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/s3_asset_deploy/manager.rb', line 141

def deploy(version_limit: 2, version_ttl: 3600, removed_ttl: 172800, clean: true, dry_run: false)
  upload(dry_run: dry_run)
  yield if block_given?
  if clean
    clean(
      dry_run: dry_run,
      version_limit: version_limit,
      version_ttl: version_ttl,
      removed_ttl: removed_ttl
    )
  end
end

#inspectObject



158
159
160
# File 'lib/s3_asset_deploy/manager.rb', line 158

def inspect
  to_s
end

#local_assets_to_uploadObject



39
40
41
42
# File 'lib/s3_asset_deploy/manager.rb', line 39

def local_assets_to_upload
  remote_asset_paths = remote_asset_collector.asset_paths
  local_asset_collector.assets.reject { |asset| remote_asset_paths.include?(asset.path) }
end

#removal_manifestObject



32
33
34
35
36
37
# File 'lib/s3_asset_deploy/manager.rb', line 32

def removal_manifest
  @removal_manifest ||= S3AssetDeploy::RemovalManifest.new(
    bucket_name,
    s3_client_options: @s3_client_options
  )
end

#to_sObject



154
155
156
# File 'lib/s3_asset_deploy/manager.rb', line 154

def to_s
  "#<#{self.class.name}:#{"0x0000%x" % (object_id << 1)} @bucket_name='#{bucket_name}'>"
end

#upload(dry_run: false) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/s3_asset_deploy/manager.rb', line 44

def upload(dry_run: false)
  verify_no_duplicate_assets!

  removal_manifest.load
  assets_to_upload = local_assets_to_upload

  (removal_manifest.keys & local_asset_collector.asset_paths).each do |path|
    log "#{path} has been re-added. Deleting from removal manifest."
    removal_manifest.delete(path) unless dry_run
  end

  uploaded_assets = []
  assets_to_upload.each do |asset|
    next unless File.file?(asset.full_path)
    log "Uploading #{asset.path}..."
    upload_asset(asset) unless dry_run
    uploaded_assets << asset.path
  end

  removal_manifest.save unless dry_run
  remote_asset_collector.clear_cache
  uploaded_assets
end