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 +4 -4
- data/CHANGELOG.md +15 -0
- data/bin/dam +9 -1
- data/lib/appydave/tools/dam/config.rb +16 -0
- data/lib/appydave/tools/dam/manifest_generator.rb +5 -3
- data/lib/appydave/tools/dam/project_listing.rb +8 -10
- data/lib/appydave/tools/dam/project_resolver.rb +31 -14
- data/lib/appydave/tools/dam/status.rb +1 -1
- data/lib/appydave/tools/dam/sync_from_ssd.rb +2 -2
- data/lib/appydave/tools/version.rb +1 -1
- data/package.json +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59166b27664a8c4643a3d5e523f54d30a58b041014dd9e22bf7e8fe5bc120991
|
|
4
|
+
data.tar.gz: 0a7ad00b2efd8076a16b61583ec09865013df2ed220da575a33593da571c6c81
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 "
|
|
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
|
-
#
|
|
111
|
-
|
|
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
|
|
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(
|
|
28
|
-
last_modified = find_last_modified(
|
|
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 =
|
|
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 =
|
|
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(
|
|
142
|
+
def self.calculate_total_size(brand, projects)
|
|
145
143
|
projects.sum do |project|
|
|
146
|
-
calculate_directory_size(
|
|
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(
|
|
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(
|
|
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(
|
|
20
|
+
return resolve_pattern(brand, project_hint) if project_hint.include?('*')
|
|
23
21
|
|
|
24
|
-
# Exact match first
|
|
25
|
-
full_path =
|
|
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
|
-
|
|
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
|
|
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(
|
|
62
|
-
|
|
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 #{
|
|
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
|
-
|
|
78
|
+
projects_dir = projects_directory(brand)
|
|
79
79
|
|
|
80
80
|
glob_pattern = pattern || '*'
|
|
81
|
-
Dir.glob("#{
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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)
|
data/package.json
CHANGED
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.
|
|
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-
|
|
11
|
+
date: 2025-11-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activemodel
|