Class: Appydave::Tools::Dam::S3Uploader

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

Overview

Handles S3 upload 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

#upload(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
79
80
81
82
83
# File 'lib/appydave/tools/dam/s3_uploader.rb', line 9

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

  unless Dir.exist?(staging_dir)
    puts "❌ No s3-staging directory found: #{staging_dir}"
    puts 'Nothing to upload.'
    return
  end

  files = Dir.glob("#{staging_dir}/**/*").select { |f| File.file?(f) }

  if files.empty?
    puts '❌ No files found in s3-staging/'
    return
  end

  puts "📦 Uploading #{files.size} file(s) from #{project_id}/s3-staging/ to S3..."
  puts ''

  uploaded = 0
  skipped = 0
  failed = 0

  # rubocop:disable Metrics/BlockLength
  files.each do |file|
    relative_path = file.sub("#{staging_dir}/", '')

    # Skip excluded files (e.g., Windows Zone.Identifier, .DS_Store)
    if excluded_path?(relative_path)
      skipped += 1
      next
    end

    s3_path = build_s3_key(relative_path)

    # Check if file already exists in S3 and compare
    s3_info = get_s3_file_info(s3_path)

    if s3_info
      s3_etag = s3_info['ETag'].gsub('"', '')
      s3_size = s3_info['Size']
      match_status = compare_files(local_file: 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 in S3 with different content"
      puts '     (multipart upload detected - comparing by size)' if multipart_etag?(s3_etag)

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

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

    if upload_file(file, s3_path, dry_run: dry_run)
      uploaded += 1
    else
      failed += 1
    end
  end
  # rubocop:enable Metrics/BlockLength

  puts ''
  puts '✅ Upload complete!'
  puts "   Uploaded: #{uploaded}, Skipped: #{skipped}, Failed: #{failed}"
end