appydave-tools 0.32.5 → 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: f5ec68e10319c834195d1d532dde3905667346a9b6f47d1f1bc9a6c572dec7ee
4
- data.tar.gz: f129cbf56dd364102c9e50b931a1004ddc207c53d5580b6205bf985e7d6dec20
3
+ metadata.gz: '0822e53f40e94e9d3c113babd747f20676c51706e0fcb8c7d4f73ab30dba1264'
4
+ data.tar.gz: fe3f9dc1576f199f0194e298e60330f56542ed235a65808c6edaacc6079a822e
5
5
  SHA512:
6
- metadata.gz: 4f7a53af93d69177a6d070d9737011a0cfa92397275897264485aad59bdd036222d87e96ebda41b3267b25abac7caf4d5e6f6448675b8375ec16a83e85636263
7
- data.tar.gz: e8d72b24314db79bd1120142f7cedd74d8ec9286dfd2ef537010d4ecfafefbaaff81a5897acdd3f769466c3c12704ead8f976015191a5305de328c754c6f8716
6
+ metadata.gz: 5ec28829f3743f5fae51f54d7a387c2c2e22a79674d94c7e9af8230f0c4f0e32bbb4cfeb7e668077a68c19cad310397d81144fb78d915641636894fef8af4e24
7
+ data.tar.gz: cad3a37461ad7158b6e44711dd3e8b3e2b34ddc70ae95b038c9e4794c072fd7326f8a9d7ee0c19e1c0ffbc6ec2f66e2a26cd3589f8cd01a0ac7f991c60824aa3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
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
+
8
+ ## [0.32.5](https://github.com/appydave/appydave-tools/compare/v0.32.4...v0.32.5) (2025-11-21)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * use original brand key in Config.project_path calls for projects_subfolder support - ensures correct path construction for brands with subfolder setting ([3966f16](https://github.com/appydave/appydave-tools/commit/3966f16a0465b9508bf899fa77a619ae6c0ea94a))
14
+
1
15
  ## [0.32.4](https://github.com/appydave/appydave-tools/compare/v0.32.3...v0.32.4) (2025-11-21)
2
16
 
3
17
 
data/bin/dam CHANGED
@@ -1125,7 +1125,7 @@ class VatCLI
1125
1125
  exit 1
1126
1126
  end
1127
1127
 
1128
- # rubocop:disable Metrics/MethodLength
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}"
@@ -1161,7 +1178,12 @@ class VatCLI
1161
1178
 
1162
1179
  manifest = JSON.parse(File.read(manifest_path), symbolize_names: true)
1163
1180
 
1164
- # Merge S3 scan data into manifest
1181
+ # Identify matched and orphaned S3 projects
1182
+ local_project_ids = manifest[:projects].map { |p| p[:id] }
1183
+ matched_projects = results.slice(*local_project_ids)
1184
+ orphaned_projects = results.reject { |project_id, _| local_project_ids.include?(project_id) }
1185
+
1186
+ # Merge S3 scan data into manifest for matched projects
1165
1187
  updated_count = 0
1166
1188
  manifest[:projects].each do |project|
1167
1189
  project_id = project[:id]
@@ -1179,23 +1201,68 @@ class VatCLI
1179
1201
  # Write updated manifest
1180
1202
  File.write(manifest_path, JSON.pretty_generate(manifest))
1181
1203
 
1204
+ # Display table
1205
+ display_s3_scan_table(matched_projects, orphaned_projects, bucket, prefix, region)
1206
+
1207
+ # Summary
1182
1208
  total_manifest_projects = manifest[:projects].size
1209
+ missing_count = total_manifest_projects - matched_projects.size
1183
1210
 
1184
1211
  puts ''
1185
- puts "✅ Updated manifest with S3 data: #{manifest_path}"
1186
- puts " Projects in S3: #{results.size}"
1187
- puts " Projects in manifest: #{total_manifest_projects}"
1188
- puts " Updated #{updated_count} projects in manifest"
1189
- if results.size != total_manifest_projects
1190
- puts ''
1191
- puts " âš ī¸ Mismatch: #{results.size} S3 projects vs #{total_manifest_projects} local projects"
1192
- puts " This may indicate projects exist locally but haven't been uploaded to S3 yet"
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
1219
+
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)
1236
+ end
1237
+
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)
1193
1249
  end
1250
+
1194
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
1195
1262
  end
1196
- # rubocop:enable Metrics/MethodLength
1263
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Style/FormatStringToken
1197
1264
 
1198
- # rubocop:disable Metrics/MethodLength
1265
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
1199
1266
  def scan_all_brands_s3
1200
1267
  Appydave::Tools::Configuration::Config.configure
1201
1268
  brands_config = Appydave::Tools::Configuration::Config.brands
@@ -1236,7 +1303,20 @@ class VatCLI
1236
1303
  puts ''
1237
1304
  puts "Total brands scanned: #{successful.size}/#{results.size}"
1238
1305
  end
1239
- # rubocop:enable Metrics/MethodLength
1306
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
1307
+
1308
+ # Format bytes in human-readable format
1309
+ # rubocop:disable Style/FormatStringToken
1310
+ def format_bytes(bytes)
1311
+ return '0 B' if bytes.zero?
1312
+
1313
+ units = %w[B KB MB GB TB]
1314
+ exp = (Math.log(bytes) / Math.log(1024)).to_i
1315
+ exp = [exp, units.length - 1].min
1316
+
1317
+ format('%.1f %s', bytes.to_f / (1024**exp), units[exp])
1318
+ end
1319
+ # rubocop:enable Style/FormatStringToken
1240
1320
  end
1241
1321
 
1242
1322
  # Run CLI
@@ -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.32.5'
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.32.5",
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.32.5
4
+ version: 0.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys