appydave-tools 0.29.0 → 0.31.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: 76cae4ca64c8ad4d198bb5de8811c6f00fb75001cd85570dd8de70a693b0f1e5
4
- data.tar.gz: 14aa751842fa98ebccfd996d36ca941b3cf0a61101f60990eff617f2cc96680c
3
+ metadata.gz: 59166b27664a8c4643a3d5e523f54d30a58b041014dd9e22bf7e8fe5bc120991
4
+ data.tar.gz: 0a7ad00b2efd8076a16b61583ec09865013df2ed220da575a33593da571c6c81
5
5
  SHA512:
6
- metadata.gz: d11e5479bdca80a75562dd8f228367fd5085efb1f53f72c0fad6da3f12384e035892eb56a25dfd1933055bf8334e1ead7e8fe12454dfc769a069f748cf3d75c0
7
- data.tar.gz: a508233c8fd6963ada3e79a6b4ef8a21de6ff5e07728fe62d5b763a73d1ff1d2f39acc6c642c2b075baa0fc9989941cfff625bb8c91bb60212fd7cdbc14e2b1c
6
+ metadata.gz: c7b247b7916f728f0903cdc563da3dce0b2f0f1378dc6afe860d2d22de8c3dddbe5489ed09af1857c518686553f64bbc1cc9202e085ff4770bdfd4becaacd863
7
+ data.tar.gz: d088f0367d0b3105cd9e69a0c02fa9b58163920c75cabb25bae56be18a01294900c7c5522bcff2bfb2ac2cb1600e7903fa9d7a732946f053d175392f130ab63a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ # [0.30.0](https://github.com/appydave/appydave-tools/compare/v0.29.0...v0.30.0) (2025-11-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * add Config.project_path utility method for DRY principle - update all DAM files to use centralized path resolution ([acd9b9a](https://github.com/appydave/appydave-tools/commit/acd9b9afb0b03af0bb980ab1208aacf9ed422267))
7
+
8
+ # [0.29.0](https://github.com/appydave/appydave-tools/compare/v0.28.0...v0.29.0) (2025-11-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * add comprehensive unit tests for recent S3 features - Zone.Identifier exclusion, download timing, project directory creation, and excluded_path helper ([2a2f35a](https://github.com/appydave/appydave-tools/commit/2a2f35acb1e3674c15860120f0cf60a3fe0fe6b2))
14
+ * add unit tests for projects_subfolder feature in S3Operations ([7b47ee2](https://github.com/appydave/appydave-tools/commit/7b47ee20e1f4326446833ccd87fbb441cbabba45))
15
+
1
16
  # [0.28.0](https://github.com/appydave/appydave-tools/compare/v0.27.0...v0.28.0) (2025-11-20)
2
17
 
3
18
 
data/bin/dam CHANGED
@@ -1179,10 +1179,18 @@ class VatCLI
1179
1179
  # Write updated manifest
1180
1180
  File.write(manifest_path, JSON.pretty_generate(manifest))
1181
1181
 
1182
+ total_manifest_projects = manifest[:projects].size
1183
+
1182
1184
  puts ''
1183
1185
  puts "✅ Updated manifest with S3 data: #{manifest_path}"
1184
- puts " Scanned #{results.size} projects in S3"
1186
+ puts " Projects in S3: #{results.size}"
1187
+ puts " Projects in manifest: #{total_manifest_projects}"
1185
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"
1193
+ end
1186
1194
  puts ''
1187
1195
  end
1188
1196
  # rubocop:enable Metrics/MethodLength
@@ -43,6 +43,22 @@ module Appydave
43
43
  path
44
44
  end
45
45
 
46
+ # Get the full path to a project directory, respecting brand's projects_subfolder setting
47
+ # @param brand_key [String] Brand key (e.g., 'appydave', 'supportsignal')
48
+ # @param project_id [String] Project ID (e.g., 'b64-bmad-claude-sdk', 'a01-shocking-stat-v1')
49
+ # @return [String] Absolute path to project directory
50
+ def project_path(brand_key, project_id)
51
+ Appydave::Tools::Configuration::Config.configure
52
+ brand_info = Appydave::Tools::Configuration::Config.brands.get_brand(brand_key)
53
+ brand_dir = brand_path(brand_key)
54
+
55
+ if brand_info.settings.projects_subfolder && !brand_info.settings.projects_subfolder.empty?
56
+ File.join(brand_dir, brand_info.settings.projects_subfolder, project_id)
57
+ else
58
+ File.join(brand_dir, project_id)
59
+ end
60
+ end
61
+
46
62
  # Get git remote URL for a brand (with self-healing)
47
63
  # @param brand_key [String] Brand key (e.g., 'appydave', 'voz')
48
64
  # @return [String, nil] Git remote URL or nil if not a git repo
@@ -107,11 +107,13 @@ module Appydave
107
107
  end
108
108
 
109
109
  # Scan local flat structure (active projects only)
110
- # Check both brand root and projects subfolder if configured
111
- scan_paths = [brand_path]
110
+ # If projects_subfolder is configured, scan only that subfolder
111
+ # Otherwise, scan brand root (for flat structure like AppyDave)
112
112
  if brand_info.settings.projects_subfolder && !brand_info.settings.projects_subfolder.empty?
113
113
  projects_folder = File.join(brand_path, brand_info.settings.projects_subfolder)
114
- scan_paths << projects_folder if Dir.exist?(projects_folder)
114
+ scan_paths = Dir.exist?(projects_folder) ? [projects_folder] : []
115
+ else
116
+ scan_paths = [brand_path]
115
117
  end
116
118
 
117
119
  scan_paths.each do |scan_path|
@@ -24,8 +24,8 @@ module Appydave
24
24
  brand_data = brands.map do |brand|
25
25
  brand_path = Config.brand_path(brand)
26
26
  projects = ProjectResolver.list_projects(brand)
27
- total_size = calculate_total_size(brand_path, projects)
28
- last_modified = find_last_modified(brand_path, projects)
27
+ total_size = calculate_total_size(brand, projects)
28
+ last_modified = find_last_modified(brand, projects)
29
29
 
30
30
  {
31
31
  name: brand,
@@ -63,11 +63,9 @@ module Appydave
63
63
  return
64
64
  end
65
65
 
66
- brand_path = Config.brand_path(brand)
67
-
68
66
  # Gather project data
69
67
  project_data = projects.map do |project|
70
- project_path = File.join(brand_path, project)
68
+ project_path = Config.project_path(brand, project)
71
69
  size = calculate_directory_size(project_path)
72
70
  modified = File.mtime(project_path)
73
71
 
@@ -109,7 +107,7 @@ module Appydave
109
107
 
110
108
  # Gather project data
111
109
  project_data = matches.map do |project|
112
- project_path = File.join(brand_path, project)
110
+ project_path = Config.project_path(brand, project)
113
111
  size = calculate_directory_size(project_path)
114
112
  modified = File.mtime(project_path)
115
113
 
@@ -141,9 +139,9 @@ module Appydave
141
139
  # Helper methods
142
140
 
143
141
  # Calculate total size of all projects in a brand
144
- def self.calculate_total_size(brand_path, projects)
142
+ def self.calculate_total_size(brand, projects)
145
143
  projects.sum do |project|
146
- calculate_directory_size(File.join(brand_path, project))
144
+ calculate_directory_size(Config.project_path(brand, project))
147
145
  end
148
146
  end
149
147
 
@@ -161,11 +159,11 @@ module Appydave
161
159
  end
162
160
 
163
161
  # Find the most recent modification time across all projects
164
- def self.find_last_modified(brand_path, projects)
162
+ def self.find_last_modified(brand, projects)
165
163
  return Time.at(0) if projects.empty?
166
164
 
167
165
  projects.map do |project|
168
- File.mtime(File.join(brand_path, project))
166
+ File.mtime(Config.project_path(brand, project))
169
167
  end.max
170
168
  end
171
169
 
@@ -16,18 +16,17 @@ module Appydave
16
16
  # @param project_hint [String] Project name or pattern (e.g., 'b65', 'boy-baker', 'b6*')
17
17
  # @return [String, Array<String>] Full project name or array of names for patterns
18
18
  def resolve(brand, project_hint)
19
- brand_path = Config.brand_path(brand)
20
-
21
19
  # Check for pattern (wildcard)
22
- return resolve_pattern(brand_path, project_hint) if project_hint.include?('*')
20
+ return resolve_pattern(brand, project_hint) if project_hint.include?('*')
23
21
 
24
- # Exact match first
25
- full_path = File.join(brand_path, project_hint)
22
+ # Exact match first - use Config.project_path to respect projects_subfolder
23
+ full_path = Config.project_path(brand, project_hint)
26
24
  return project_hint if Dir.exist?(full_path)
27
25
 
28
26
  # FliVideo pattern: b65 → b65-*
29
27
  if project_hint =~ /^[a-z]\d+$/
30
- matches = Dir.glob("#{brand_path}/#{project_hint}-*")
28
+ projects_dir = projects_directory(brand)
29
+ matches = Dir.glob("#{projects_dir}/#{project_hint}-*")
31
30
  .select { |path| File.directory?(path) }
32
31
  .map { |path| File.basename(path) }
33
32
 
@@ -55,17 +54,18 @@ module Appydave
55
54
  end
56
55
 
57
56
  # Resolve pattern to list of matching projects
58
- # @param brand_path [String] Full path to brand directory
57
+ # @param brand [String] Brand shortcut or full name
59
58
  # @param pattern [String] Pattern with wildcards (e.g., 'b6*')
60
59
  # @return [Array<String>] List of matching project names
61
- def resolve_pattern(brand_path, pattern)
62
- matches = Dir.glob("#{brand_path}/#{pattern}")
60
+ def resolve_pattern(brand, pattern)
61
+ projects_dir = projects_directory(brand)
62
+ matches = Dir.glob("#{projects_dir}/#{pattern}")
63
63
  .select { |path| File.directory?(path) }
64
64
  .select { |path| valid_project?(path) }
65
65
  .map { |path| File.basename(path) }
66
66
  .sort
67
67
 
68
- raise "❌ No projects found matching pattern '#{pattern}' in #{brand_path}" if matches.empty?
68
+ raise "❌ No projects found matching pattern '#{pattern}' in #{projects_dir}" if matches.empty?
69
69
 
70
70
  matches
71
71
  end
@@ -75,10 +75,10 @@ module Appydave
75
75
  # @param pattern [String, nil] Optional filter pattern
76
76
  # @return [Array<String>] List of project names
77
77
  def list_projects(brand, pattern = nil)
78
- brand_path = Config.brand_path(brand)
78
+ projects_dir = projects_directory(brand)
79
79
 
80
80
  glob_pattern = pattern || '*'
81
- Dir.glob("#{brand_path}/#{glob_pattern}")
81
+ Dir.glob("#{projects_dir}/#{glob_pattern}")
82
82
  .select { |path| File.directory?(path) }
83
83
  .select { |path| valid_project?(path) }
84
84
  .map { |path| File.basename(path) }
@@ -121,10 +121,27 @@ module Appydave
121
121
  # @param project [String] Project name
122
122
  # @return [Boolean] true if project directory exists
123
123
  def project_exists?(brand, project)
124
- projects_root = Config.projects_root
125
- project_path = File.join(projects_root, brand, project)
124
+ project_path = Config.project_path(brand, project)
126
125
  Dir.exist?(project_path)
127
126
  end
127
+
128
+ private
129
+
130
+ # Get the directory where projects are stored for a brand
131
+ # Respects projects_subfolder setting
132
+ # @param brand [String] Brand shortcut or full name
133
+ # @return [String] Path to directory containing projects
134
+ def projects_directory(brand)
135
+ Appydave::Tools::Configuration::Config.configure
136
+ brand_info = Appydave::Tools::Configuration::Config.brands.get_brand(brand)
137
+ brand_path = Config.brand_path(brand)
138
+
139
+ if brand_info.settings.projects_subfolder && !brand_info.settings.projects_subfolder.empty?
140
+ File.join(brand_path, brand_info.settings.projects_subfolder)
141
+ else
142
+ brand_path
143
+ end
144
+ end
128
145
  end
129
146
  end
130
147
  end
@@ -36,7 +36,7 @@ module Appydave
36
36
  def resolve_project_path(project_id)
37
37
  # Resolve short name if needed (b65 -> b65-full-name)
38
38
  resolved = ProjectResolver.resolve(brand, project_id)
39
- File.join(brand_path, resolved)
39
+ Config.project_path(brand, resolved)
40
40
  end
41
41
 
42
42
  def show_project_status
@@ -165,8 +165,8 @@ module Appydave
165
165
 
166
166
  return { skipped: 1, files: 0, bytes: 0, reason: 'SSD path not found' } unless Dir.exist?(ssd_path)
167
167
 
168
- # Check for flat folder conflict (stale manifest)
169
- flat_path = File.join(brand_path, project_id)
168
+ # Check for flat folder conflict (stale manifest) - use Config.project_path to respect projects_subfolder
169
+ flat_path = Config.project_path(brand, project_id)
170
170
  return { skipped: 1, files: 0, bytes: 0, reason: 'Flat folder exists (stale manifest?)' } if Dir.exist?(flat_path)
171
171
 
172
172
  # Determine local destination path (archived structure)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.29.0'
5
+ VERSION = '0.31.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.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "AppyDave YouTube Automation Tools",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appydave-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.0
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-20 00:00:00.000000000 Z
11
+ date: 2025-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel