Class: Appydave::Tools::Dam::S3Downloader

Inherits:
S3Base
  • Object
show all
Defined in:
lib/appydave/tools/dam/s3_downloader.rb

Overview

Handles S3 download operations. Inherits shared infrastructure and helpers from S3Base.

Constant Summary

Constants inherited from S3Base

Appydave::Tools::Dam::S3Base::EXCLUDE_PATTERNS

Instance Attribute Summary

Attributes inherited from S3Base

#brand, #brand_info, #brand_path, #project_id

Instance Method Summary collapse

Methods inherited from S3Base

#build_s3_key, #extract_relative_path, #initialize, #s3_client

Constructor Details

This class inherits a constructor from Appydave::Tools::Dam::S3Base

Instance Method Details

#download(dry_run: false) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
77
78
# File 'lib/appydave/tools/dam/s3_downloader.rb', line 9

def download(dry_run: false)
  project_dir = project_directory_path
  staging_dir = File.join(project_dir, 's3-staging')

  # Ensure project directory exists before download
  unless Dir.exist?(project_dir)
    puts "📁 Creating project directory: #{project_id}"
    FileUtils.mkdir_p(project_dir) unless dry_run
  end

  s3_files = list_s3_files

  if s3_files.empty?
    puts "❌ No files found in S3 for #{brand}/#{project_id}"
    return
  end

  total_size = s3_files.sum { |f| f['Size'] || 0 }
  puts "📦 Downloading #{s3_files.size} file(s) (#{file_size_human(total_size)}) from S3 to #{project_id}/s3-staging/..."
  puts ''

  downloaded = 0
  skipped = 0
  failed = 0

  # rubocop:disable Metrics/BlockLength
  s3_files.each do |s3_file|
    key = s3_file['Key']
    relative_path = extract_relative_path(key)
    local_file = File.join(staging_dir, relative_path)

    # Check if file already exists and compare
    s3_etag = s3_file['ETag'].gsub('"', '')
    s3_size = s3_file['Size']

    if File.exist?(local_file)
      match_status = compare_files(local_file: local_file, s3_etag: s3_etag, s3_size: s3_size)

      if match_status == :synced
        comparison_method = multipart_etag?(s3_etag) ? 'size match' : 'unchanged'
        puts "  ⏭️  Skipped: #{relative_path} (#{comparison_method})"
        skipped += 1
        next
      end

      # File exists but content differs - warn before overwriting
      puts "  ⚠️  Warning: #{relative_path} exists locally with different content"
      puts '     (multipart upload detected - comparing by size)' if multipart_etag?(s3_etag)

      if s3_file['LastModified']
        s3_time = s3_file['LastModified']
        local_time = File.mtime(local_file)
        puts "     S3: #{s3_time.strftime('%Y-%m-%d %H:%M')} | Local: #{local_time.strftime('%Y-%m-%d %H:%M')}"

        puts '     ⚠️  Local file is NEWER than S3 - you may be overwriting recent changes!' if local_time > s3_time
      end
      puts '     Downloading will overwrite local version...'
    end

    if download_file(key, local_file, dry_run: dry_run)
      downloaded += 1
    else
      failed += 1
    end
  end
  # rubocop:enable Metrics/BlockLength
  puts ''
  puts '✅ Download complete!'
  puts "   Downloaded: #{downloaded}, Skipped: #{skipped}, Failed: #{failed}"
end