appydave-tools 0.33.0 → 0.34.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7dae60a45e72f5c3a8b551750c4728a037ee8584b436989dd700daa78f3ee8a1
4
- data.tar.gz: 7b26dbf29f2d47524832504a2ef5fd2a021efd9a458a61477a19dccd22a56fb0
3
+ metadata.gz: '0822e53f40e94e9d3c113babd747f20676c51706e0fcb8c7d4f73ab30dba1264'
4
+ data.tar.gz: fe3f9dc1576f199f0194e298e60330f56542ed235a65808c6edaacc6079a822e
5
5
  SHA512:
6
- metadata.gz: 36090c84bffcf2e1e0dc581b8af16d3adc2e2a89eaf2a0c47a264479006d7365b8099ee1998ec6b15e360797108bb42632b09edeadef84479f2c1d8734f401d8
7
- data.tar.gz: 84e6011c4bc9ad9d4ec692b147d82542df34413a4faccd7056964bf6c5b2ae394328368db6a4b7a9f7c12f10503fd3a56f30476212f06733a6fbe70dc9f359ea
6
+ metadata.gz: 5ec28829f3743f5fae51f54d7a387c2c2e22a79674d94c7e9af8230f0c4f0e32bbb4cfeb7e668077a68c19cad310397d81144fb78d915641636894fef8af4e24
7
+ data.tar.gz: cad3a37461ad7158b6e44711dd3e8b3e2b34ddc70ae95b038c9e4794c072fd7326f8a9d7ee0c19e1c0ffbc6ec2f66e2a26cd3589f8cd01a0ac7f991c60824aa3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [0.33.0](https://github.com/appydave/appydave-tools/compare/v0.32.5...v0.33.0) (2025-11-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * add orphaned S3 folder detection in s3-scan command - separates matched projects from orphaned folders with file count and size info for easier cleanup identification ([3aa03aa](https://github.com/appydave/appydave-tools/commit/3aa03aab5e57ca55015d226b39fcb4f3203d4ca4))
7
+
1
8
  ## [0.32.5](https://github.com/appydave/appydave-tools/compare/v0.32.4...v0.32.5) (2025-11-21)
2
9
 
3
10
 
data/bin/dam CHANGED
@@ -1125,7 +1125,7 @@ class VatCLI
1125
1125
  exit 1
1126
1126
  end
1127
1127
 
1128
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1128
+ # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
1129
1129
  def scan_single_brand_s3(brand_arg)
1130
1130
  puts "🔄 Scanning S3 for #{brand_arg}..."
1131
1131
  puts ''
@@ -1133,11 +1133,30 @@ class VatCLI
1133
1133
  brand_key = brand_arg
1134
1134
  scanner = Appydave::Tools::Dam::S3Scanner.new(brand_key)
1135
1135
 
1136
- # Scan all projects
1137
- results = scanner.scan_all_projects
1136
+ # Get brand info for S3 path
1137
+ Appydave::Tools::Configuration::Config.configure
1138
+ brand_info = Appydave::Tools::Configuration::Config.brands.get_brand(brand_key)
1139
+ bucket = brand_info.aws.s3_bucket
1140
+ prefix = brand_info.aws.s3_prefix
1141
+ region = brand_info.aws.region
1142
+
1143
+ # Spinner characters for progress
1144
+ spinner_chars = ['⠋', '⠙', 'â š', 'â ¸', 'â ŧ', 'â ´', 'â Ļ', 'â §', '⠇', '⠏']
1145
+ spinner_index = 0
1146
+
1147
+ print "🔍 Scanning s3://#{bucket}/#{prefix}\n"
1148
+ print ' Scanning projects... '
1149
+
1150
+ # Scan all projects with progress callback
1151
+ results = scanner.scan_all_projects(show_progress: false) do |current, total|
1152
+ print "\r Scanning projects... #{spinner_chars[spinner_index]} (#{current}/#{total})"
1153
+ spinner_index = (spinner_index + 1) % spinner_chars.length
1154
+ end
1155
+
1156
+ print "\r Scanning projects... ✓ (#{results.size} found)\n"
1157
+ puts ''
1138
1158
 
1139
1159
  if results.empty?
1140
- puts ''
1141
1160
  puts "âš ī¸ No projects found in S3 for #{brand_key}"
1142
1161
  puts ' This may indicate:'
1143
1162
  puts ' - No files uploaded to S3 yet'
@@ -1147,12 +1166,10 @@ class VatCLI
1147
1166
  end
1148
1167
 
1149
1168
  # Load existing manifest
1150
- Appydave::Tools::Configuration::Config.configure
1151
1169
  brand_path = Appydave::Tools::Dam::Config.brand_path(brand_key)
1152
1170
  manifest_path = File.join(brand_path, 'projects.json')
1153
1171
 
1154
1172
  unless File.exist?(manifest_path)
1155
- puts ''
1156
1173
  puts "❌ Manifest not found: #{manifest_path}"
1157
1174
  puts " Run: dam manifest #{brand_key}"
1158
1175
  puts " Then retry: dam s3-scan #{brand_key}"
@@ -1163,8 +1180,8 @@ class VatCLI
1163
1180
 
1164
1181
  # Identify matched and orphaned S3 projects
1165
1182
  local_project_ids = manifest[:projects].map { |p| p[:id] }
1166
- matched_s3_projects = results.keys.select { |project_id| local_project_ids.include?(project_id) }
1167
- orphaned_s3_projects = results.keys.reject { |project_id| local_project_ids.include?(project_id) }
1183
+ matched_projects = results.slice(*local_project_ids)
1184
+ orphaned_projects = results.reject { |project_id, _| local_project_ids.include?(project_id) }
1168
1185
 
1169
1186
  # Merge S3 scan data into manifest for matched projects
1170
1187
  updated_count = 0
@@ -1184,37 +1201,68 @@ class VatCLI
1184
1201
  # Write updated manifest
1185
1202
  File.write(manifest_path, JSON.pretty_generate(manifest))
1186
1203
 
1204
+ # Display table
1205
+ display_s3_scan_table(matched_projects, orphaned_projects, bucket, prefix, region)
1206
+
1207
+ # Summary
1187
1208
  total_manifest_projects = manifest[:projects].size
1209
+ missing_count = total_manifest_projects - matched_projects.size
1188
1210
 
1189
1211
  puts ''
1190
- puts "✅ Updated manifest with S3 data: #{manifest_path}"
1191
- puts " Projects in S3: #{results.size}"
1192
- puts " Projects in manifest: #{total_manifest_projects}"
1193
- puts " Updated #{updated_count} projects in manifest"
1212
+ puts 'â„šī¸ Summary:'
1213
+ puts " â€ĸ Updated #{updated_count} projects in manifest"
1214
+ puts " â€ĸ #{missing_count} local project(s) not yet uploaded to S3" if missing_count.positive?
1215
+ puts " â€ĸ Manifest: #{manifest_path}"
1216
+ puts ''
1217
+ end
1218
+ # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/AbcSize
1194
1219
 
1195
- if orphaned_s3_projects.any?
1196
- puts ''
1197
- puts ' âš ī¸ Orphaned S3 folders (no local project):'
1198
- orphaned_s3_projects.each do |project_id|
1199
- s3_data = results[project_id]
1200
- size = format_bytes(s3_data[:total_bytes])
1201
- puts " - #{project_id} (#{s3_data[:file_count]} files, #{size})"
1202
- end
1203
- puts ''
1204
- puts ' These folders exist in S3 but have no corresponding local project.'
1205
- puts ' Consider cleaning them up with: dam s3-cleanup'
1220
+ # Display S3 scan results in table format
1221
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Style/FormatStringToken
1222
+ def display_s3_scan_table(matched_projects, orphaned_projects, bucket, prefix, region)
1223
+ puts '✅ S3 Projects Report'
1224
+ puts ''
1225
+ puts 'PROJECT FILES SIZE STATUS LAST MODIFIED'
1226
+ puts '-' * 80
1227
+
1228
+ # Display matched projects first (sorted alphabetically)
1229
+ matched_projects.sort.each do |project_id, data|
1230
+ files = data[:file_count].to_s.rjust(5)
1231
+ size = format_bytes(data[:total_bytes]).rjust(10)
1232
+ status = '✓'
1233
+ modified = data[:last_modified] ? Time.parse(data[:last_modified]).strftime('%Y-%m-%d %H:%M') : 'N/A'
1234
+
1235
+ puts format('%-26s %5s %10s %s %s', project_id, files, size, status, modified)
1206
1236
  end
1207
1237
 
1208
- if matched_s3_projects.size < total_manifest_projects
1209
- missing_count = total_manifest_projects - matched_s3_projects.size
1210
- puts ''
1211
- puts " â„šī¸ #{missing_count} local project(s) not yet uploaded to S3"
1238
+ # Display orphaned projects (sorted alphabetically)
1239
+ return if orphaned_projects.empty?
1240
+
1241
+ puts '-' * 80
1242
+ orphaned_projects.sort.each do |project_id, data|
1243
+ files = data[:file_count].to_s.rjust(5)
1244
+ size = format_bytes(data[:total_bytes]).rjust(10)
1245
+ status = '❌'
1246
+ modified = data[:last_modified] ? Time.parse(data[:last_modified]).strftime('%Y-%m-%d %H:%M') : 'N/A'
1247
+
1248
+ puts format('%-26s %5s %10s %s %s', project_id, files, size, status, modified)
1212
1249
  end
1250
+
1213
1251
  puts ''
1252
+ folder_word = orphaned_projects.size > 1 ? 'folders' : 'folder'
1253
+ puts "âš ī¸ #{orphaned_projects.size} orphaned #{folder_word} found (no local project)"
1254
+
1255
+ orphaned_projects.sort.each do |project_id, _data|
1256
+ # Build AWS Console URL
1257
+ project_prefix = "#{prefix}#{project_id}/"
1258
+ console_url = "https://#{region}.console.aws.amazon.com/s3/buckets/#{bucket}?prefix=#{project_prefix}&region=#{region}"
1259
+ puts " → #{project_id}"
1260
+ puts " #{console_url}"
1261
+ end
1214
1262
  end
1215
- # rubocop:enable Metrics/MethodLength
1263
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Style/FormatStringToken
1216
1264
 
1217
- # rubocop:disable Metrics/MethodLength
1265
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
1218
1266
  def scan_all_brands_s3
1219
1267
  Appydave::Tools::Configuration::Config.configure
1220
1268
  brands_config = Appydave::Tools::Configuration::Config.brands
@@ -1255,7 +1303,7 @@ class VatCLI
1255
1303
  puts ''
1256
1304
  puts "Total brands scanned: #{successful.size}/#{results.size}"
1257
1305
  end
1258
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
1306
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
1259
1307
 
1260
1308
  # Format bytes in human-readable format
1261
1309
  # rubocop:disable Style/FormatStringToken
@@ -17,12 +17,13 @@ module Appydave
17
17
 
18
18
  # Scan S3 for a specific project
19
19
  # @param project_id [String] Project ID (e.g., "b65-guy-monroe-marketing-plan")
20
+ # @param show_progress [Boolean] Whether to show progress output (default: true)
20
21
  # @return [Hash] S3 file data with :file_count, :total_bytes, :last_modified
21
- def scan_project(project_id)
22
+ def scan_project(project_id, show_progress: true)
22
23
  bucket = @brand_info.aws.s3_bucket
23
24
  prefix = File.join(@brand_info.aws.s3_prefix, project_id, '')
24
25
 
25
- puts " 🔍 Scanning #{project_id}..."
26
+ puts " 🔍 Scanning #{project_id}..." if show_progress
26
27
 
27
28
  files = list_s3_objects(bucket, prefix)
28
29
 
@@ -50,28 +51,23 @@ module Appydave
50
51
  end
51
52
 
52
53
  # Scan all projects in brand's S3 bucket
54
+ # @param show_progress [Boolean] Whether to show detailed progress (default: true)
53
55
  # @return [Hash] Map of project_id => scan result
54
- def scan_all_projects
56
+ def scan_all_projects(show_progress: true, &progress_callback)
55
57
  bucket = @brand_info.aws.s3_bucket
56
58
  prefix = @brand_info.aws.s3_prefix
57
59
 
58
- puts "🔍 Scanning all projects in S3: s3://#{bucket}/#{prefix}"
59
- puts ''
60
-
61
60
  # List all "directories" (prefixes) under brand prefix
62
61
  project_prefixes = list_s3_prefixes(bucket, prefix)
63
62
 
64
- if project_prefixes.empty?
65
- puts ' 📭 No projects found in S3'
66
- return {}
67
- end
68
-
69
- puts " Found #{project_prefixes.size} projects in S3"
70
- puts ''
63
+ return {} if project_prefixes.empty?
71
64
 
72
65
  results = {}
73
- project_prefixes.each do |project_id|
74
- results[project_id] = scan_project(project_id)
66
+ project_prefixes.each_with_index do |project_id, index|
67
+ # Call progress callback if provided (for spinner)
68
+ progress_callback&.call(index + 1, project_prefixes.size)
69
+
70
+ results[project_id] = scan_project(project_id, show_progress: show_progress)
75
71
  end
76
72
 
77
73
  results
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.33.0'
5
+ VERSION = '0.34.0'
6
6
  end
7
7
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.33.0",
3
+ "version": "0.34.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.33.0
4
+ version: 0.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys