appydave-tools 0.18.5 → 0.20.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.
@@ -0,0 +1,278 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Appydave
6
+ module Tools
7
+ module Dam
8
+ # Show unified status for video project (local, S3, SSD, git)
9
+ class Status
10
+ attr_reader :brand, :project_id, :brand_info, :brand_path, :project_path
11
+
12
+ def initialize(brand, project_id = nil)
13
+ @brand_info = load_brand_info(brand)
14
+ @brand = @brand_info.key
15
+ @brand_path = Config.brand_path(@brand)
16
+ @project_id = project_id
17
+ @project_path = project_id ? resolve_project_path(project_id) : nil
18
+ end
19
+
20
+ # Show status for project or brand
21
+ def show
22
+ if project_id
23
+ show_project_status
24
+ else
25
+ show_brand_status
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def load_brand_info(brand)
32
+ Appydave::Tools::Configuration::Config.configure
33
+ Appydave::Tools::Configuration::Config.brands.get_brand(brand)
34
+ end
35
+
36
+ def resolve_project_path(project_id)
37
+ # Resolve short name if needed (b65 -> b65-full-name)
38
+ resolved = ProjectResolver.new.resolve(brand, project_id)
39
+ File.join(brand_path, resolved)
40
+ end
41
+
42
+ def show_project_status
43
+ puts "📊 Status: v-#{brand}/#{File.basename(project_path)}"
44
+ puts ''
45
+
46
+ manifest = load_manifest
47
+ project_entry = find_project_in_manifest(manifest)
48
+
49
+ unless project_entry
50
+ puts '❌ Project not found in manifest'
51
+ puts " Run: dam manifest #{brand}"
52
+ return
53
+ end
54
+
55
+ show_storage_status(project_entry)
56
+ show_git_status if git_repo?
57
+ end
58
+
59
+ def show_brand_status
60
+ puts "📊 Brand Status: v-#{brand}"
61
+ puts ''
62
+
63
+ # Show git remote (with self-healing)
64
+ remote = Config.git_remote(brand)
65
+ if remote
66
+ puts "📡 Git Remote: #{remote}"
67
+ else
68
+ puts '📡 Git Remote: Not configured (not a git repository)'
69
+ end
70
+ puts ''
71
+
72
+ # Show git status
73
+ if git_repo?
74
+ show_brand_git_status
75
+ else
76
+ puts 'Git: Not a git repository'
77
+ end
78
+ puts ''
79
+
80
+ # Show manifest summary
81
+ manifest = load_manifest
82
+ if manifest
83
+ show_manifest_summary(manifest)
84
+ else
85
+ puts '❌ Manifest not found'
86
+ puts " Run: dam manifest #{brand}"
87
+ end
88
+ end
89
+
90
+ def show_storage_status(project_entry)
91
+ puts 'Storage:'
92
+
93
+ # Local storage
94
+ show_local_status(project_entry)
95
+
96
+ # S3 staging (inferred - only show if exists)
97
+ show_s3_status(project_entry) if project_entry[:storage][:s3][:exists]
98
+
99
+ # SSD backup (inferred - only show if configured and exists)
100
+ show_ssd_status(project_entry) if brand_info.locations.ssd_backup &&
101
+ brand_info.locations.ssd_backup != 'NOT-SET' &&
102
+ project_entry[:storage][:ssd][:exists]
103
+
104
+ puts ''
105
+ end
106
+
107
+ def show_local_status(project_entry)
108
+ local = project_entry[:storage][:local]
109
+
110
+ if local[:exists]
111
+ puts " 📁 Local: ✓ exists (#{local[:structure]} structure)"
112
+ puts " Heavy files: #{local[:has_heavy_files] ? 'yes' : 'no'}"
113
+ puts " Light files: #{local[:has_light_files] ? 'yes' : 'no'}"
114
+ else
115
+ puts ' 📁 Local: ✗ does not exist'
116
+ end
117
+ puts ''
118
+ end
119
+
120
+ def show_s3_status(_project_entry)
121
+ puts ' ☁️ S3 Staging: ✓ exists'
122
+
123
+ # TODO: Query S3 for detailed status (files needing sync)
124
+ # For now, just show that s3-staging folder exists locally
125
+ s3_staging_path = File.join(project_path, 's3-staging')
126
+ if Dir.exist?(s3_staging_path)
127
+ file_count = Dir.glob(File.join(s3_staging_path, '*')).count
128
+ puts " Local staging files: #{file_count}"
129
+ end
130
+ puts ''
131
+ end
132
+
133
+ def show_ssd_status(project_entry)
134
+ puts ' 💾 SSD Backup: ✓ exists'
135
+ puts " Path: #{project_entry[:storage][:ssd][:path]}"
136
+ puts ''
137
+ end
138
+
139
+ def show_git_status
140
+ puts 'Git:'
141
+
142
+ status = git_status_info
143
+
144
+ puts " 🌿 Branch: #{status[:branch]}"
145
+ puts " 📡 Remote: #{status[:remote]}" if status[:remote]
146
+
147
+ if status[:modified_count].positive? || status[:untracked_count].positive?
148
+ puts " ↕️ Status: #{status[:modified_count]} modified, #{status[:untracked_count]} untracked"
149
+ else
150
+ puts ' ↕️ Status: Clean working directory'
151
+ end
152
+
153
+ if status[:ahead].positive? || status[:behind].positive?
154
+ puts " 🔄 Sync: #{sync_status_text(status[:ahead], status[:behind])}"
155
+ else
156
+ puts ' 🔄 Sync: Up to date'
157
+ end
158
+
159
+ puts ''
160
+ end
161
+
162
+ def show_brand_git_status
163
+ status = git_status_info
164
+
165
+ puts "🌿 Branch: #{status[:branch]}"
166
+ puts "📡 Remote: #{status[:remote]}" if status[:remote]
167
+
168
+ if status[:modified_count].positive? || status[:untracked_count].positive?
169
+ puts "↕️ Changes: #{status[:modified_count]} modified, #{status[:untracked_count]} untracked"
170
+ else
171
+ puts '✓ Working directory clean'
172
+ end
173
+
174
+ if status[:ahead].positive? || status[:behind].positive?
175
+ puts "🔄 Sync: #{sync_status_text(status[:ahead], status[:behind])}"
176
+ else
177
+ puts '✓ Up to date with remote'
178
+ end
179
+ end
180
+
181
+ def show_manifest_summary(manifest)
182
+ puts '📋 Manifest Summary:'
183
+ puts " Total projects: #{manifest[:projects].size}"
184
+
185
+ local_count = manifest[:projects].count { |p| p[:storage][:local][:exists] }
186
+ s3_count = manifest[:projects].count { |p| p[:storage][:s3][:exists] }
187
+ ssd_count = manifest[:projects].count { |p| p[:storage][:ssd][:exists] }
188
+
189
+ puts " Local: #{local_count}"
190
+ puts " S3 staging: #{s3_count}"
191
+ puts " SSD backup: #{ssd_count}"
192
+
193
+ # Project types
194
+ storyline_count = manifest[:projects].count { |p| p[:hasStorylineJson] }
195
+ puts ''
196
+ puts " Storyline projects: #{storyline_count}"
197
+ puts " FliVideo projects: #{manifest[:projects].size - storyline_count}"
198
+ end
199
+
200
+ def sync_status_text(ahead, behind)
201
+ parts = []
202
+ parts << "#{ahead} ahead" if ahead.positive?
203
+ parts << "#{behind} behind" if behind.positive?
204
+ parts.join(', ')
205
+ end
206
+
207
+ def load_manifest
208
+ manifest_path = File.join(brand_path, 'projects.json')
209
+ return nil unless File.exist?(manifest_path)
210
+
211
+ JSON.parse(File.read(manifest_path), symbolize_names: true)
212
+ rescue JSON::ParserError
213
+ nil
214
+ end
215
+
216
+ def find_project_in_manifest(manifest)
217
+ return nil unless manifest
218
+
219
+ project_name = File.basename(project_path)
220
+ manifest[:projects].find { |p| p[:id] == project_name }
221
+ end
222
+
223
+ def git_repo?
224
+ git_dir = File.join(brand_path, '.git')
225
+ Dir.exist?(git_dir)
226
+ end
227
+
228
+ def git_status_info
229
+ {
230
+ branch: current_branch,
231
+ remote: remote_url,
232
+ modified_count: modified_files_count,
233
+ untracked_count: untracked_files_count,
234
+ ahead: commits_ahead,
235
+ behind: commits_behind
236
+ }
237
+ end
238
+
239
+ def current_branch
240
+ `git -C "#{brand_path}" rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
241
+ rescue StandardError
242
+ 'unknown'
243
+ end
244
+
245
+ def remote_url
246
+ result = `git -C "#{brand_path}" remote get-url origin 2>/dev/null`.strip
247
+ result.empty? ? nil : result
248
+ rescue StandardError
249
+ nil
250
+ end
251
+
252
+ def modified_files_count
253
+ `git -C "#{brand_path}" status --porcelain 2>/dev/null | grep -E "^.M|^M" | wc -l`.strip.to_i
254
+ rescue StandardError
255
+ 0
256
+ end
257
+
258
+ def untracked_files_count
259
+ `git -C "#{brand_path}" status --porcelain 2>/dev/null | grep -E "^\\?\\?" | wc -l`.strip.to_i
260
+ rescue StandardError
261
+ 0
262
+ end
263
+
264
+ def commits_ahead
265
+ `git -C "#{brand_path}" rev-list --count @{upstream}..HEAD 2>/dev/null`.strip.to_i
266
+ rescue StandardError
267
+ 0
268
+ end
269
+
270
+ def commits_behind
271
+ `git -C "#{brand_path}" rev-list --count HEAD..@{upstream} 2>/dev/null`.strip.to_i
272
+ rescue StandardError
273
+ 0
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Appydave
4
4
  module Tools
5
- VERSION = '0.18.5'
5
+ VERSION = '0.20.0'
6
6
  end
7
7
  end
@@ -59,6 +59,10 @@ require 'appydave/tools/dam/s3_operations'
59
59
  require 'appydave/tools/dam/project_listing'
60
60
  require 'appydave/tools/dam/manifest_generator'
61
61
  require 'appydave/tools/dam/sync_from_ssd'
62
+ require 'appydave/tools/dam/status'
63
+ require 'appydave/tools/dam/repo_status'
64
+ require 'appydave/tools/dam/repo_sync'
65
+ require 'appydave/tools/dam/repo_push'
62
66
 
63
67
  require 'appydave/tools/youtube_automation/gpt_agent'
64
68
 
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appydave-tools",
3
- "version": "0.18.5",
3
+ "version": "0.20.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.18.5
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
@@ -243,6 +243,7 @@ files:
243
243
  - docs/configuration/settings.example.json
244
244
  - docs/dam/dam-testing-plan.md
245
245
  - docs/dam/dam-vision.md
246
+ - docs/dam/prd-git-integration.md
246
247
  - docs/dam/session-summary-2025-11-09.md
247
248
  - docs/dam/usage.md
248
249
  - docs/dam/windows-testing-guide.md
@@ -291,7 +292,11 @@ files:
291
292
  - lib/appydave/tools/dam/manifest_generator.rb
292
293
  - lib/appydave/tools/dam/project_listing.rb
293
294
  - lib/appydave/tools/dam/project_resolver.rb
295
+ - lib/appydave/tools/dam/repo_push.rb
296
+ - lib/appydave/tools/dam/repo_status.rb
297
+ - lib/appydave/tools/dam/repo_sync.rb
294
298
  - lib/appydave/tools/dam/s3_operations.rb
299
+ - lib/appydave/tools/dam/status.rb
295
300
  - lib/appydave/tools/dam/sync_from_ssd.rb
296
301
  - lib/appydave/tools/debuggable.rb
297
302
  - lib/appydave/tools/gpt_context/_doc.md