appydave-tools 0.18.5 → 0.19.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 +7 -0
- data/docs/dam/prd-git-integration.md +871 -0
- data/lib/appydave/tools/configuration/models/brands_config.rb +4 -1
- data/lib/appydave/tools/dam/config.rb +38 -0
- data/lib/appydave/tools/dam/manifest_generator.rb +13 -0
- data/lib/appydave/tools/version.rb +1 -1
- data/package.json +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1d9c19286530b337db5170902dce174c889b50a7c2425f6e209efec097a0a81
|
|
4
|
+
data.tar.gz: 220392dd2eec9910e3c57e7c7de4a372303ab261d01a2db9d6c64769368bf71e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 33dd5ec557282ce787d457cfaa6e4b996fae6d82b2b4ac4aa5e531ac17f1c310f000fd905428ce3fb969b35635aea9122347a7760e73f0c6282425ffce6e65e9
|
|
7
|
+
data.tar.gz: 5be8054544c85a36ef280ed6aa85096f386243df5e66c48f911728fe943350d66c69863b0005424df97e20697bc75126a37e1c4ce84cd8265728ca017e58a4e0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [0.18.5](https://github.com/appydave/appydave-tools/compare/v0.18.4...v0.18.5) (2025-11-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* update test plan to reflect all DAM commands completed (manifest, archive, sync-ssd) and clarify git repo scripts ([ac1436a](https://github.com/appydave/appydave-tools/commit/ac1436a09c4ae32009f1b98dc697da9a87f1c4ee))
|
|
7
|
+
|
|
1
8
|
## [0.18.4](https://github.com/appydave/appydave-tools/compare/v0.18.3...v0.18.4) (2025-11-10)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -0,0 +1,871 @@
|
|
|
1
|
+
# PRD: Git Integration & Unified Status
|
|
2
|
+
|
|
3
|
+
**Status:** Draft
|
|
4
|
+
**Author:** David Cruwys
|
|
5
|
+
**Created:** 2025-11-10
|
|
6
|
+
**Last Updated:** 2025-11-10
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
DAM (Digital Asset Management) currently manages video projects across three storage layers: local disk, S3 cloud collaboration, and SSD archival. However, video projects also exist as git repositories for version control of light files (subtitles, images, markdown, metadata). Currently, git operations are handled by separate shell scripts (`status-all.sh`, `sync-all.sh`, `clone-all.sh`) outside the DAM system.
|
|
13
|
+
|
|
14
|
+
This PRD proposes integrating git repository management into DAM, creating a unified interface for managing both storage layers (heavy video files + light versioned files) from a single command-line tool.
|
|
15
|
+
|
|
16
|
+
### Current State
|
|
17
|
+
|
|
18
|
+
**Dual Management Systems:**
|
|
19
|
+
- **DAM layer** - Manages heavy files (video) via S3/SSD sync commands
|
|
20
|
+
- **Git layer** - Manages light files (SRT, images, docs) via separate shell scripts
|
|
21
|
+
|
|
22
|
+
**Problem:** Two separate workflows, no unified view of project state across both layers
|
|
23
|
+
|
|
24
|
+
**Example current workflow:**
|
|
25
|
+
```bash
|
|
26
|
+
# Check video file sync
|
|
27
|
+
dam s3-status appydave b65
|
|
28
|
+
|
|
29
|
+
# Separately check git status
|
|
30
|
+
cd /video-projects/v-appydave/b65-project
|
|
31
|
+
git status
|
|
32
|
+
|
|
33
|
+
# Separately sync git repo
|
|
34
|
+
cd /video-projects/v-appydave
|
|
35
|
+
git pull
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Proposed State
|
|
39
|
+
|
|
40
|
+
**Unified DAM Interface:**
|
|
41
|
+
```bash
|
|
42
|
+
# Single command shows everything: local, S3, SSD, and git status
|
|
43
|
+
dam status appydave b65
|
|
44
|
+
|
|
45
|
+
# Git operations integrated into DAM
|
|
46
|
+
dam repo-status appydave # Check all projects
|
|
47
|
+
dam repo-sync appydave # Pull updates for all projects
|
|
48
|
+
dam repo-push appydave b65 # Push specific project
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Goals
|
|
54
|
+
|
|
55
|
+
### Primary Goals
|
|
56
|
+
|
|
57
|
+
1. **Unified Status View** - Single command shows project state across all layers (local, S3, SSD, git)
|
|
58
|
+
2. **Git Integration** - Manage git operations through DAM commands (status, sync, push)
|
|
59
|
+
3. **Self-Healing Config** - Automatically infer and populate git remote URLs when missing
|
|
60
|
+
4. **S3 Tracking** - Add S3 staging to manifest for complete storage visibility
|
|
61
|
+
|
|
62
|
+
### Secondary Goals
|
|
63
|
+
|
|
64
|
+
5. **Dynamic Brand Discovery** - Eliminate hardcoded brand lists, use brands.json
|
|
65
|
+
6. **Inferred Behavior** - Status command shows relevant info based on what exists
|
|
66
|
+
7. **Consistent Interface** - Git commands follow same patterns as S3 commands
|
|
67
|
+
|
|
68
|
+
### Non-Goals
|
|
69
|
+
|
|
70
|
+
- **File-level git operations** - Not implementing `dam git commit/add/etc` (use git directly)
|
|
71
|
+
- **Complex git workflows** - Not handling branches, rebasing, merging (use git directly)
|
|
72
|
+
- **GitHub API integration** - Not creating issues, PRs, releases
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## User Stories
|
|
77
|
+
|
|
78
|
+
### Story 1: Check Project Status (Primary Use Case)
|
|
79
|
+
|
|
80
|
+
**As a content creator,**
|
|
81
|
+
**I want to see complete project status in one command,**
|
|
82
|
+
**So I don't have to check multiple tools to understand project state.**
|
|
83
|
+
|
|
84
|
+
**Current workflow:**
|
|
85
|
+
```bash
|
|
86
|
+
# Check S3 sync status
|
|
87
|
+
dam s3-status appydave b65
|
|
88
|
+
|
|
89
|
+
# Check SSD archive status
|
|
90
|
+
cd /video-projects/v-appydave && ls b65* && cd /Volumes/T7/youtube-PUBLISHED/appydave && ls b65*
|
|
91
|
+
|
|
92
|
+
# Check git status
|
|
93
|
+
cd /video-projects/v-appydave/b65-project
|
|
94
|
+
git status
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Proposed workflow:**
|
|
98
|
+
```bash
|
|
99
|
+
dam status appydave b65
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Output:**
|
|
103
|
+
```
|
|
104
|
+
📊 Status: v-appydave/b65-guy-monroe-marketing-plan
|
|
105
|
+
|
|
106
|
+
Storage:
|
|
107
|
+
📁 Local: ✓ exists (flat structure)
|
|
108
|
+
Heavy files: no
|
|
109
|
+
Light files: yes (5 SRT, 12 images, 3 docs)
|
|
110
|
+
|
|
111
|
+
☁️ S3 Staging: ✓ exists
|
|
112
|
+
Files in sync: 3
|
|
113
|
+
Need upload: 2 (15.3 MB)
|
|
114
|
+
Need download: 0
|
|
115
|
+
|
|
116
|
+
💾 SSD Backup: ✓ exists
|
|
117
|
+
Last archived: 2025-11-08
|
|
118
|
+
|
|
119
|
+
Git:
|
|
120
|
+
🌿 Branch: main
|
|
121
|
+
📡 Remote: git@github.com:klueless-io/v-appydave.git
|
|
122
|
+
↕️ Status: 2 files modified, 1 untracked
|
|
123
|
+
🔄 Sync: Behind by 0 commits
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Acceptance Criteria:**
|
|
127
|
+
- Shows local/S3/SSD/git status in single view
|
|
128
|
+
- Displays only relevant sections (skips S3 if no s3-staging folder)
|
|
129
|
+
- Color-coded indicators (✓/✗/⚠️)
|
|
130
|
+
- Human-readable file counts and sizes
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### Story 2: Sync All Brand Repos (Team Collaboration)
|
|
135
|
+
|
|
136
|
+
**As a team member,**
|
|
137
|
+
**I want to pull updates for all brand repositories at once,**
|
|
138
|
+
**So I can start work with latest changes across all projects.**
|
|
139
|
+
|
|
140
|
+
**Current workflow:**
|
|
141
|
+
```bash
|
|
142
|
+
cd /video-projects
|
|
143
|
+
./v-shared/sync-all.sh # Hardcoded REPOS array
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Proposed workflow:**
|
|
147
|
+
```bash
|
|
148
|
+
dam repo-sync appydave
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Output:**
|
|
152
|
+
```
|
|
153
|
+
🔄 Syncing git repositories for appydave...
|
|
154
|
+
|
|
155
|
+
📦 v-appydave
|
|
156
|
+
✓ Already up to date
|
|
157
|
+
|
|
158
|
+
Summary:
|
|
159
|
+
Repos checked: 1
|
|
160
|
+
Updated: 0
|
|
161
|
+
Already current: 1
|
|
162
|
+
Errors: 0
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Acceptance Criteria:**
|
|
166
|
+
- Uses brands.json (no hardcoded lists)
|
|
167
|
+
- Runs `git pull` on brand directory
|
|
168
|
+
- Shows summary of changes
|
|
169
|
+
- Handles errors gracefully (uncommitted changes, merge conflicts)
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### Story 3: Self-Healing Git Remote (Bootstrap Scenario)
|
|
174
|
+
|
|
175
|
+
**As a new team member,**
|
|
176
|
+
**I want git remote URLs auto-populated,**
|
|
177
|
+
**So I don't have to manually configure repos.**
|
|
178
|
+
|
|
179
|
+
**Scenario:**
|
|
180
|
+
```bash
|
|
181
|
+
# brands.json initially has: "git_remote": null
|
|
182
|
+
dam repo-status appydave
|
|
183
|
+
|
|
184
|
+
# DAM automatically:
|
|
185
|
+
# 1. Detects git_remote is null
|
|
186
|
+
# 2. Runs: cd /video-projects/v-appydave && git remote get-url origin
|
|
187
|
+
# 3. Finds: git@github.com:klueless-io/v-appydave.git
|
|
188
|
+
# 4. Updates brands.json: "git_remote": "git@github.com:klueless-io/v-appydave.git"
|
|
189
|
+
# 5. Continues with status command
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Acceptance Criteria:**
|
|
193
|
+
- Infers remote URL from existing git repo
|
|
194
|
+
- Auto-saves to brands.json (with backup)
|
|
195
|
+
- Gracefully handles non-git folders (leaves null)
|
|
196
|
+
- Only runs once (subsequent calls use cached value)
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### Story 4: Push Specific Project Changes
|
|
201
|
+
|
|
202
|
+
**As a content creator,**
|
|
203
|
+
**I want to push changes for a specific project,**
|
|
204
|
+
**So I don't accidentally push unrelated work.**
|
|
205
|
+
|
|
206
|
+
**Current workflow:**
|
|
207
|
+
```bash
|
|
208
|
+
cd /video-projects/v-appydave/b65-project
|
|
209
|
+
git add .
|
|
210
|
+
git commit -m "Add subtitles for chapter 3"
|
|
211
|
+
git push
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Proposed workflow:**
|
|
215
|
+
```bash
|
|
216
|
+
# Add and commit done with git (not DAM)
|
|
217
|
+
cd /video-projects/v-appydave/b65-project
|
|
218
|
+
git add *.srt && git commit -m "Add subtitles for chapter 3"
|
|
219
|
+
|
|
220
|
+
# Push via DAM (validates project exists in manifest)
|
|
221
|
+
dam repo-push appydave b65
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Acceptance Criteria:**
|
|
225
|
+
- Resolves project short name (b65 → b65-guy-monroe-marketing-plan)
|
|
226
|
+
- Validates project in manifest
|
|
227
|
+
- Runs `git push` from project directory
|
|
228
|
+
- Shows push result (commits pushed, branch tracking)
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Requirements
|
|
233
|
+
|
|
234
|
+
### Functional Requirements
|
|
235
|
+
|
|
236
|
+
#### FR1: Git Remote Configuration
|
|
237
|
+
|
|
238
|
+
**FR1.1** - Add `git_remote` field to brands.json schema
|
|
239
|
+
- Type: string or null
|
|
240
|
+
- Optional field (can be null for non-git brands)
|
|
241
|
+
- Example: `"git_remote": "git@github.com:klueless-io/v-appydave.git"`
|
|
242
|
+
|
|
243
|
+
**FR1.2** - Self-healing git remote inference
|
|
244
|
+
- If `git_remote` is null, attempt to infer from git command
|
|
245
|
+
- Command: `git -C <brand_path> remote get-url origin`
|
|
246
|
+
- Auto-save inferred value to brands.json (with backup)
|
|
247
|
+
- Gracefully handle non-git folders (leave null, no error)
|
|
248
|
+
|
|
249
|
+
**FR1.3** - Update brands.json documentation
|
|
250
|
+
- Add `git_remote` to example configs
|
|
251
|
+
- Document self-healing behavior
|
|
252
|
+
- Explain null vs URL states
|
|
253
|
+
|
|
254
|
+
#### FR2: S3 Staging Tracking in Manifest
|
|
255
|
+
|
|
256
|
+
**FR2.1** - Add S3 storage to manifest schema
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"id": "b65-guy-monroe-marketing-plan",
|
|
260
|
+
"storage": {
|
|
261
|
+
"local": { "exists": true, "structure": "flat", ... },
|
|
262
|
+
"ssd": { "exists": false, "path": null },
|
|
263
|
+
"s3": { "exists": true }
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**FR2.2** - Detect S3 staging presence in manifest_generator
|
|
269
|
+
- Check if `s3-staging/` directory exists
|
|
270
|
+
- Update `s3: { exists: true/false }` in manifest
|
|
271
|
+
|
|
272
|
+
**FR2.3** - Update manifest when S3 commands run
|
|
273
|
+
- `dam s3-up` → sets `s3.exists = true` after upload
|
|
274
|
+
- `dam s3-down` → sets `s3.exists = true` after download
|
|
275
|
+
- `dam s3-cleanup-remote` → sets `s3.exists = false` after cleanup
|
|
276
|
+
|
|
277
|
+
#### FR3: Unified Status Command
|
|
278
|
+
|
|
279
|
+
**FR3.1** - Create `dam status [brand] [project]` command
|
|
280
|
+
- Shows local, S3, SSD, and git status in unified view
|
|
281
|
+
- Auto-detects brand/project from PWD if not provided
|
|
282
|
+
- Inferred display (only shows relevant sections)
|
|
283
|
+
|
|
284
|
+
**FR3.2** - Storage status section
|
|
285
|
+
- Local: exists, structure type, file counts (heavy/light)
|
|
286
|
+
- S3: exists, sync status, files needing upload/download
|
|
287
|
+
- SSD: exists, last archived date
|
|
288
|
+
|
|
289
|
+
**FR3.3** - Git status section (live query, not stored)
|
|
290
|
+
- Branch name
|
|
291
|
+
- Remote URL
|
|
292
|
+
- Modified/untracked file counts
|
|
293
|
+
- Commits ahead/behind remote
|
|
294
|
+
- Skip section if not a git repo
|
|
295
|
+
|
|
296
|
+
**FR3.4** - Inferred behavior
|
|
297
|
+
- If no S3 staging folder → skip S3 section
|
|
298
|
+
- If not a git repo → skip git section
|
|
299
|
+
- If no SSD path configured → skip SSD section
|
|
300
|
+
|
|
301
|
+
#### FR4: Git Repository Commands
|
|
302
|
+
|
|
303
|
+
**FR4.1** - `dam repo-status [brand]` - Check git status for brand repos
|
|
304
|
+
- Shows git status for brand directory
|
|
305
|
+
- Option: `--all` to show status for all brands
|
|
306
|
+
- Uses git_remote from brands.json (triggers self-healing if null)
|
|
307
|
+
|
|
308
|
+
**FR4.2** - `dam repo-sync [brand]` - Pull updates for brand repos
|
|
309
|
+
- Runs `git pull` on brand directory
|
|
310
|
+
- Option: `--all` to sync all brands
|
|
311
|
+
- Handles errors (uncommitted changes, merge conflicts)
|
|
312
|
+
- Summary: repos checked, updated, errors
|
|
313
|
+
|
|
314
|
+
**FR4.3** - `dam repo-push [brand] [project]` - Push project changes
|
|
315
|
+
- Resolves project short name (b65 → full name)
|
|
316
|
+
- Validates project exists in manifest
|
|
317
|
+
- Runs `git push` from brand directory
|
|
318
|
+
- Shows commits pushed and branch tracking
|
|
319
|
+
|
|
320
|
+
**FR4.4** - Dynamic brand discovery
|
|
321
|
+
- Use brands.json to get list of brands (no hardcoded arrays)
|
|
322
|
+
- Automatically supports new brands added to config
|
|
323
|
+
|
|
324
|
+
### Non-Functional Requirements
|
|
325
|
+
|
|
326
|
+
**NFR1: Performance**
|
|
327
|
+
- Status command completes in < 2 seconds for single project
|
|
328
|
+
- repo-sync for all brands completes in < 10 seconds
|
|
329
|
+
|
|
330
|
+
**NFR2: Error Handling**
|
|
331
|
+
- Graceful failures (git not installed, repo not found, network errors)
|
|
332
|
+
- Clear error messages with remediation steps
|
|
333
|
+
- No data loss on config updates (backup system)
|
|
334
|
+
|
|
335
|
+
**NFR3: Backward Compatibility**
|
|
336
|
+
- Existing commands continue to work unchanged
|
|
337
|
+
- brands.json without git_remote field still works (self-healing)
|
|
338
|
+
- Manifest without s3 field still works (regenerate manifest)
|
|
339
|
+
|
|
340
|
+
**NFR4: Documentation**
|
|
341
|
+
- Update usage.md with new commands
|
|
342
|
+
- Update test plan with Phase 4 tests
|
|
343
|
+
- Add examples to PRD and usage guide
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Technical Design
|
|
348
|
+
|
|
349
|
+
### Architecture
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
353
|
+
│ DAM CLI │
|
|
354
|
+
│ (dam status, dam repo-status, dam repo-sync, dam repo-push) │
|
|
355
|
+
└─────────────────────────────────────────────────────────────┘
|
|
356
|
+
│
|
|
357
|
+
├── Uses brands.json (git_remote)
|
|
358
|
+
├── Uses manifest (local/S3/SSD state)
|
|
359
|
+
└── Queries git (live status, not stored)
|
|
360
|
+
|
|
361
|
+
┌─────────────┬─────────────┬─────────────┬─────────────┐
|
|
362
|
+
│ Local │ S3 │ SSD │ Git │
|
|
363
|
+
│ Storage │ Staging │ Archive │ Repos │
|
|
364
|
+
└─────────────┴─────────────┴─────────────┴─────────────┘
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Data Model Changes
|
|
368
|
+
|
|
369
|
+
#### brands.json (add git_remote field)
|
|
370
|
+
|
|
371
|
+
**Before:**
|
|
372
|
+
```json
|
|
373
|
+
{
|
|
374
|
+
"brands": [
|
|
375
|
+
{
|
|
376
|
+
"key": "appydave",
|
|
377
|
+
"shortcut": "ad",
|
|
378
|
+
"name": "AppyDave",
|
|
379
|
+
"youtube_handle": "@appydave",
|
|
380
|
+
"locations": {
|
|
381
|
+
"video_projects": "/Users/davidcruwys/dev/video-projects/v-appydave",
|
|
382
|
+
"ssd_backup": "/Volumes/T7/youtube-PUBLISHED/appydave"
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**After:**
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"brands": [
|
|
393
|
+
{
|
|
394
|
+
"key": "appydave",
|
|
395
|
+
"shortcut": "ad",
|
|
396
|
+
"name": "AppyDave",
|
|
397
|
+
"youtube_handle": "@appydave",
|
|
398
|
+
"git_remote": "git@github.com:klueless-io/v-appydave.git",
|
|
399
|
+
"locations": {
|
|
400
|
+
"video_projects": "/Users/davidcruwys/dev/video-projects/v-appydave",
|
|
401
|
+
"ssd_backup": "/Volumes/T7/youtube-PUBLISHED/appydave"
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"key": "voz",
|
|
406
|
+
"shortcut": "voz",
|
|
407
|
+
"name": "VOZ",
|
|
408
|
+
"youtube_handle": "@voz",
|
|
409
|
+
"git_remote": null,
|
|
410
|
+
"locations": {
|
|
411
|
+
"video_projects": "/Users/davidcruwys/dev/video-projects/v-voz",
|
|
412
|
+
"ssd_backup": "NOT-SET"
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
**Notes:**
|
|
420
|
+
- `git_remote` is optional (can be null)
|
|
421
|
+
- Self-healing: If null, DAM attempts to infer and populate
|
|
422
|
+
- Non-git brands: Leave as null (valid state)
|
|
423
|
+
|
|
424
|
+
#### projects.json (add S3 staging field)
|
|
425
|
+
|
|
426
|
+
**Before:**
|
|
427
|
+
```json
|
|
428
|
+
{
|
|
429
|
+
"id": "b65-guy-monroe-marketing-plan",
|
|
430
|
+
"storage": {
|
|
431
|
+
"local": {
|
|
432
|
+
"exists": true,
|
|
433
|
+
"structure": "flat",
|
|
434
|
+
"has_heavy_files": false,
|
|
435
|
+
"has_light_files": true
|
|
436
|
+
},
|
|
437
|
+
"ssd": {
|
|
438
|
+
"exists": false,
|
|
439
|
+
"path": null
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**After:**
|
|
446
|
+
```json
|
|
447
|
+
{
|
|
448
|
+
"id": "b65-guy-monroe-marketing-plan",
|
|
449
|
+
"storage": {
|
|
450
|
+
"local": {
|
|
451
|
+
"exists": true,
|
|
452
|
+
"structure": "flat",
|
|
453
|
+
"has_heavy_files": false,
|
|
454
|
+
"has_light_files": true
|
|
455
|
+
},
|
|
456
|
+
"s3": {
|
|
457
|
+
"exists": true
|
|
458
|
+
},
|
|
459
|
+
"ssd": {
|
|
460
|
+
"exists": false,
|
|
461
|
+
"path": null
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**Notes:**
|
|
468
|
+
- `s3.exists` is boolean (true/false)
|
|
469
|
+
- Updated by manifest_generator (checks for s3-staging/ directory)
|
|
470
|
+
- Updated by S3 commands (s3-up, s3-down, s3-cleanup-remote)
|
|
471
|
+
|
|
472
|
+
### Self-Healing Git Remote Logic
|
|
473
|
+
|
|
474
|
+
**Flow:**
|
|
475
|
+
```ruby
|
|
476
|
+
def get_git_remote(brand_info)
|
|
477
|
+
# 1. Check brands.json
|
|
478
|
+
return brand_info.git_remote if brand_info.git_remote.present?
|
|
479
|
+
|
|
480
|
+
# 2. Attempt inference
|
|
481
|
+
brand_path = Config.brand_path(brand_info.key)
|
|
482
|
+
remote_url = infer_git_remote(brand_path)
|
|
483
|
+
|
|
484
|
+
# 3. Auto-save if inferred
|
|
485
|
+
if remote_url
|
|
486
|
+
update_brand_git_remote(brand_info.key, remote_url)
|
|
487
|
+
return remote_url
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# 4. Non-git folder (leave null)
|
|
491
|
+
nil
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def infer_git_remote(brand_path)
|
|
495
|
+
result = `git -C #{brand_path} remote get-url origin 2>/dev/null`.strip
|
|
496
|
+
result.empty? ? nil : result
|
|
497
|
+
rescue
|
|
498
|
+
nil
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
def update_brand_git_remote(brand_key, remote_url)
|
|
502
|
+
brands_config = Appydave::Tools::Configuration::Config.brands
|
|
503
|
+
brand = brands_config.brands.find { |b| b.key == brand_key }
|
|
504
|
+
brand.git_remote = remote_url
|
|
505
|
+
brands_config.save # Uses backup system
|
|
506
|
+
end
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Git Status Query (Live, Not Stored)
|
|
510
|
+
|
|
511
|
+
**Why live query?**
|
|
512
|
+
- Git status changes frequently (commits, pulls, edits)
|
|
513
|
+
- Storing in manifest causes staleness issues
|
|
514
|
+
- Query is fast (< 100ms for typical repo)
|
|
515
|
+
|
|
516
|
+
**Implementation:**
|
|
517
|
+
```ruby
|
|
518
|
+
def git_status(brand_path)
|
|
519
|
+
return nil unless git_repo?(brand_path)
|
|
520
|
+
|
|
521
|
+
{
|
|
522
|
+
branch: current_branch(brand_path),
|
|
523
|
+
remote: remote_url(brand_path),
|
|
524
|
+
modified_files: modified_count(brand_path),
|
|
525
|
+
untracked_files: untracked_count(brand_path),
|
|
526
|
+
ahead: commits_ahead(brand_path),
|
|
527
|
+
behind: commits_behind(brand_path)
|
|
528
|
+
}
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
def current_branch(path)
|
|
532
|
+
`git -C #{path} rev-parse --abbrev-ref HEAD`.strip
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def commits_ahead(path)
|
|
536
|
+
`git -C #{path} rev-list --count @{upstream}..HEAD 2>/dev/null`.strip.to_i
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def commits_behind(path)
|
|
540
|
+
`git -C #{path} rev-list --count HEAD..@{upstream} 2>/dev/null`.strip.to_i
|
|
541
|
+
end
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Unified Status Command Design
|
|
545
|
+
|
|
546
|
+
**Command:** `dam status [brand] [project]`
|
|
547
|
+
|
|
548
|
+
**Output format:**
|
|
549
|
+
```
|
|
550
|
+
📊 Status: v-appydave/b65-guy-monroe-marketing-plan
|
|
551
|
+
|
|
552
|
+
Storage:
|
|
553
|
+
📁 Local: ✓ exists (flat structure)
|
|
554
|
+
Heavy files: no
|
|
555
|
+
Light files: yes (5 SRT, 12 images, 3 docs)
|
|
556
|
+
|
|
557
|
+
☁️ S3 Staging: ✓ exists
|
|
558
|
+
Files in sync: 3
|
|
559
|
+
Need upload: 2 (15.3 MB)
|
|
560
|
+
Need download: 0
|
|
561
|
+
|
|
562
|
+
💾 SSD Backup: ✓ exists
|
|
563
|
+
Last archived: 2025-11-08
|
|
564
|
+
|
|
565
|
+
Git:
|
|
566
|
+
🌿 Branch: main
|
|
567
|
+
📡 Remote: git@github.com:klueless-io/v-appydave.git
|
|
568
|
+
↕️ Status: 2 files modified, 1 untracked
|
|
569
|
+
🔄 Sync: Behind by 0 commits
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Inferred display logic:**
|
|
573
|
+
```ruby
|
|
574
|
+
def show_status(brand, project)
|
|
575
|
+
manifest = load_manifest(brand)
|
|
576
|
+
project_entry = manifest.projects.find { |p| p.id == project }
|
|
577
|
+
|
|
578
|
+
# Always show local (required)
|
|
579
|
+
show_local_status(project_entry)
|
|
580
|
+
|
|
581
|
+
# Show S3 only if s3-staging exists
|
|
582
|
+
show_s3_status(brand, project) if project_entry.storage.s3.exists
|
|
583
|
+
|
|
584
|
+
# Show SSD only if ssd_backup configured
|
|
585
|
+
show_ssd_status(project_entry) if brand_info.locations.ssd_backup != "NOT-SET"
|
|
586
|
+
|
|
587
|
+
# Show git only if git repo detected
|
|
588
|
+
show_git_status(brand_path) if git_repo?(brand_path)
|
|
589
|
+
end
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## Implementation Plan
|
|
595
|
+
|
|
596
|
+
### Phase 1: Configuration & Manifest (Foundation)
|
|
597
|
+
|
|
598
|
+
**Tasks:**
|
|
599
|
+
1. Add `git_remote` field to Brand model in Configuration module
|
|
600
|
+
2. Update brands.json schema documentation
|
|
601
|
+
3. Implement self-healing git remote inference logic
|
|
602
|
+
4. Add S3 storage field to Manifest schema
|
|
603
|
+
5. Update ManifestGenerator to detect S3 staging
|
|
604
|
+
6. Add tests for config and manifest changes
|
|
605
|
+
|
|
606
|
+
**Deliverables:**
|
|
607
|
+
- Updated brands.json with git_remote field
|
|
608
|
+
- Updated manifest with S3 tracking
|
|
609
|
+
- Self-healing git remote logic working
|
|
610
|
+
- 100% test coverage for new code
|
|
611
|
+
|
|
612
|
+
**Estimated effort:** 4-6 hours
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
### Phase 2: Unified Status Command
|
|
617
|
+
|
|
618
|
+
**Tasks:**
|
|
619
|
+
1. Create `DamStatus` class
|
|
620
|
+
2. Implement storage status (local/S3/SSD)
|
|
621
|
+
3. Implement git status query (live)
|
|
622
|
+
4. Implement inferred display logic
|
|
623
|
+
5. Add CLI command `dam status`
|
|
624
|
+
6. Add tests and documentation
|
|
625
|
+
|
|
626
|
+
**Deliverables:**
|
|
627
|
+
- `dam status [brand] [project]` command working
|
|
628
|
+
- Unified output showing all layers
|
|
629
|
+
- Inferred behavior (skips irrelevant sections)
|
|
630
|
+
- Usage documentation updated
|
|
631
|
+
|
|
632
|
+
**Estimated effort:** 6-8 hours
|
|
633
|
+
|
|
634
|
+
---
|
|
635
|
+
|
|
636
|
+
### Phase 3: Git Repository Commands
|
|
637
|
+
|
|
638
|
+
**Tasks:**
|
|
639
|
+
1. Create `RepoStatus` class
|
|
640
|
+
2. Implement `dam repo-status [brand]` command
|
|
641
|
+
3. Create `RepoSync` class
|
|
642
|
+
4. Implement `dam repo-sync [brand]` command
|
|
643
|
+
5. Create `RepoPush` class
|
|
644
|
+
6. Implement `dam repo-push [brand] [project]` command
|
|
645
|
+
7. Add dynamic brand discovery (use brands.json)
|
|
646
|
+
8. Add tests and documentation
|
|
647
|
+
|
|
648
|
+
**Deliverables:**
|
|
649
|
+
- `dam repo-status`, `dam repo-sync`, `dam repo-push` commands working
|
|
650
|
+
- Dynamic brand discovery (no hardcoded lists)
|
|
651
|
+
- Error handling for git failures
|
|
652
|
+
- Usage documentation updated
|
|
653
|
+
|
|
654
|
+
**Estimated effort:** 8-10 hours
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
### Phase 4: Testing & Documentation
|
|
659
|
+
|
|
660
|
+
**Tasks:**
|
|
661
|
+
1. Add Phase 4 to test plan
|
|
662
|
+
2. Update usage.md with new commands
|
|
663
|
+
3. Create integration tests
|
|
664
|
+
4. Test self-healing behavior
|
|
665
|
+
5. Test error scenarios
|
|
666
|
+
6. Performance testing (status command < 2s)
|
|
667
|
+
|
|
668
|
+
**Deliverables:**
|
|
669
|
+
- Complete test coverage
|
|
670
|
+
- Updated documentation
|
|
671
|
+
- Performance benchmarks
|
|
672
|
+
- User acceptance testing complete
|
|
673
|
+
|
|
674
|
+
**Estimated effort:** 4-6 hours
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
**Total estimated effort:** 22-30 hours
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
## Success Criteria
|
|
683
|
+
|
|
684
|
+
### Must Have (MVP)
|
|
685
|
+
|
|
686
|
+
1. ✅ **Unified status command works** - Shows local/S3/SSD/git in single view
|
|
687
|
+
2. ✅ **Git remote self-healing works** - Auto-populates from git repo if null
|
|
688
|
+
3. ✅ **S3 tracking in manifest** - projects.json includes S3 staging state
|
|
689
|
+
4. ✅ **repo-status command works** - Shows git status for brand repos
|
|
690
|
+
5. ✅ **repo-sync command works** - Pulls updates for brand repos
|
|
691
|
+
6. ✅ **Dynamic brand discovery** - No hardcoded brand lists
|
|
692
|
+
|
|
693
|
+
### Should Have
|
|
694
|
+
|
|
695
|
+
7. ✅ **repo-push command works** - Pushes specific project changes
|
|
696
|
+
8. ✅ **Inferred display** - Status skips irrelevant sections
|
|
697
|
+
9. ✅ **Error handling** - Graceful failures with clear messages
|
|
698
|
+
10. ✅ **Documentation complete** - Usage guide, test plan, PRD updated
|
|
699
|
+
|
|
700
|
+
### Could Have (Future)
|
|
701
|
+
|
|
702
|
+
11. ⏳ **repo-clone command** - Clone missing brand repos
|
|
703
|
+
12. ⏳ **Batch operations** - `dam repo-sync --all` for all brands
|
|
704
|
+
13. ⏳ **Status filtering** - `dam status --show-modified` (only changed files)
|
|
705
|
+
14. ⏳ **Git hooks integration** - Auto-update manifest on git push
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
## Open Questions
|
|
710
|
+
|
|
711
|
+
1. **Should `dam status` default to current project (PWD) or require explicit args?**
|
|
712
|
+
- Option A: Auto-detect from PWD (like s3-up/s3-down)
|
|
713
|
+
- Option B: Require explicit brand/project args
|
|
714
|
+
- **Recommendation:** Option A (consistent with existing commands)
|
|
715
|
+
|
|
716
|
+
2. **Should repo-push auto-detect uncommitted changes and warn?**
|
|
717
|
+
- Option A: Warn if uncommitted changes detected
|
|
718
|
+
- Option B: Let git handle it (git push won't do anything)
|
|
719
|
+
- **Recommendation:** Option A (better UX)
|
|
720
|
+
|
|
721
|
+
3. **Should manifest track S3 file-level details (file names, sizes)?**
|
|
722
|
+
- Option A: Boolean only (`s3: { exists: true }`)
|
|
723
|
+
- Option B: File inventory (`s3: { exists: true, files: [...] }`)
|
|
724
|
+
- **Recommendation:** Option A (simpler, avoids staleness)
|
|
725
|
+
|
|
726
|
+
4. **Should git_remote support multiple remotes (origin, upstream)?**
|
|
727
|
+
- Option A: Single remote only (origin)
|
|
728
|
+
- Option B: Array of remotes
|
|
729
|
+
- **Recommendation:** Option A (YAGNI - simple is better)
|
|
730
|
+
|
|
731
|
+
5. **What happens if git remote inference finds SSH URL but user needs HTTPS?**
|
|
732
|
+
- Option A: Store whatever is found, let user manually edit
|
|
733
|
+
- Option B: Prompt user to choose SSH vs HTTPS
|
|
734
|
+
- **Recommendation:** Option A (user can edit brands.json if needed)
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## Risks & Mitigations
|
|
739
|
+
|
|
740
|
+
| Risk | Impact | Mitigation |
|
|
741
|
+
|------|--------|------------|
|
|
742
|
+
| Git not installed on system | High | Check for git binary, show clear error with install instructions |
|
|
743
|
+
| Git remote inference fails | Medium | Leave git_remote as null, document manual config process |
|
|
744
|
+
| Manifest becomes stale (S3 state outdated) | Medium | Update manifest on S3 commands, regenerate if inconsistent |
|
|
745
|
+
| Performance (git status slow for large repos) | Low | Use `--porcelain` flag for faster parsing, timeout after 5s |
|
|
746
|
+
| Breaking changes to brands.json | High | Use backup system, backward compatibility (null git_remote valid) |
|
|
747
|
+
|
|
748
|
+
---
|
|
749
|
+
|
|
750
|
+
## Dependencies
|
|
751
|
+
|
|
752
|
+
- **Ruby 3.4.2** - Already in use
|
|
753
|
+
- **Git CLI** - Must be installed on system
|
|
754
|
+
- **brands.json** - Must have valid configuration
|
|
755
|
+
- **Manifest system** - Must be generated (`dam manifest`)
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Alternatives Considered
|
|
760
|
+
|
|
761
|
+
### Alternative 1: Keep git scripts separate (status quo)
|
|
762
|
+
|
|
763
|
+
**Pros:**
|
|
764
|
+
- No new code to write
|
|
765
|
+
- Simple shell scripts, easy to understand
|
|
766
|
+
|
|
767
|
+
**Cons:**
|
|
768
|
+
- Two separate tools (DAM + git scripts)
|
|
769
|
+
- No unified status view
|
|
770
|
+
- Hardcoded brand lists (maintenance burden)
|
|
771
|
+
|
|
772
|
+
**Decision:** Rejected - Integration provides better UX
|
|
773
|
+
|
|
774
|
+
### Alternative 2: Store git status in manifest
|
|
775
|
+
|
|
776
|
+
**Pros:**
|
|
777
|
+
- Faster status command (no live queries)
|
|
778
|
+
- All data in one place (manifest)
|
|
779
|
+
|
|
780
|
+
**Cons:**
|
|
781
|
+
- Staleness problem (git changes frequently)
|
|
782
|
+
- Manifest updates required after every git operation
|
|
783
|
+
- Complex synchronization logic
|
|
784
|
+
|
|
785
|
+
**Decision:** Rejected - Live queries are fast enough, staleness not worth it
|
|
786
|
+
|
|
787
|
+
### Alternative 3: Use GitHub API instead of git CLI
|
|
788
|
+
|
|
789
|
+
**Pros:**
|
|
790
|
+
- No git binary required
|
|
791
|
+
- Can check status remotely
|
|
792
|
+
|
|
793
|
+
**Cons:**
|
|
794
|
+
- Requires network connection
|
|
795
|
+
- Requires GitHub authentication
|
|
796
|
+
- Only works for GitHub (not GitLab, Bitbucket, self-hosted)
|
|
797
|
+
- Overkill for simple status checks
|
|
798
|
+
|
|
799
|
+
**Decision:** Rejected - git CLI is simpler and more universal
|
|
800
|
+
|
|
801
|
+
---
|
|
802
|
+
|
|
803
|
+
## Appendix: Example Workflows
|
|
804
|
+
|
|
805
|
+
### Workflow 1: Morning Sync Routine
|
|
806
|
+
|
|
807
|
+
**Scenario:** Team member starts work, needs to sync all repos
|
|
808
|
+
|
|
809
|
+
```bash
|
|
810
|
+
# Pull updates for all brands
|
|
811
|
+
dam repo-sync appydave
|
|
812
|
+
|
|
813
|
+
# Check unified status for active project
|
|
814
|
+
dam status appydave b65
|
|
815
|
+
|
|
816
|
+
# Work on project...
|
|
817
|
+
cd /video-projects/v-appydave/b65-guy-monroe-marketing-plan
|
|
818
|
+
# Edit subtitles, add images, etc.
|
|
819
|
+
|
|
820
|
+
# Commit changes
|
|
821
|
+
git add *.srt images/
|
|
822
|
+
git commit -m "Add chapter 3 subtitles and thumbnails"
|
|
823
|
+
|
|
824
|
+
# Push via DAM
|
|
825
|
+
dam repo-push appydave b65
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### Workflow 2: Collaboration Handoff
|
|
829
|
+
|
|
830
|
+
**Scenario:** David uploads video files to S3 for Jan to edit
|
|
831
|
+
|
|
832
|
+
```bash
|
|
833
|
+
# David: Upload raw footage
|
|
834
|
+
cd /video-projects/v-appydave/b65-guy-monroe-marketing-plan
|
|
835
|
+
mkdir -p s3-staging
|
|
836
|
+
cp ~/ecamm/chapter-3-raw.mp4 s3-staging/
|
|
837
|
+
dam s3-up appydave b65
|
|
838
|
+
|
|
839
|
+
# David: Push subtitle updates
|
|
840
|
+
git add *.srt && git commit -m "Add chapter 3 script"
|
|
841
|
+
dam repo-push appydave b65
|
|
842
|
+
|
|
843
|
+
# Jan: Pull git updates and S3 files
|
|
844
|
+
dam repo-sync appydave
|
|
845
|
+
dam s3-down appydave b65
|
|
846
|
+
|
|
847
|
+
# Jan: Check what needs work
|
|
848
|
+
dam status appydave b65
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
### Workflow 3: Project Archive & Cleanup
|
|
852
|
+
|
|
853
|
+
**Scenario:** Complete project, archive to SSD, clean up S3
|
|
854
|
+
|
|
855
|
+
```bash
|
|
856
|
+
# Check final status
|
|
857
|
+
dam status appydave b63
|
|
858
|
+
|
|
859
|
+
# Archive to SSD
|
|
860
|
+
dam archive appydave b63
|
|
861
|
+
|
|
862
|
+
# Push final git state
|
|
863
|
+
dam repo-push appydave b63
|
|
864
|
+
|
|
865
|
+
# Clean up S3 (save storage costs)
|
|
866
|
+
dam s3-cleanup-remote appydave b63 --force
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
---
|
|
870
|
+
|
|
871
|
+
**End of PRD**
|
|
@@ -117,6 +117,7 @@ module Appydave
|
|
|
117
117
|
'type' => 'owned',
|
|
118
118
|
'youtube_channels' => [],
|
|
119
119
|
'team' => [],
|
|
120
|
+
'git_remote' => nil,
|
|
120
121
|
'locations' => {
|
|
121
122
|
'video_projects' => '',
|
|
122
123
|
'ssd_backup' => ''
|
|
@@ -144,7 +145,7 @@ module Appydave
|
|
|
144
145
|
|
|
145
146
|
# Type-safe class to access brand properties
|
|
146
147
|
class BrandInfo
|
|
147
|
-
attr_accessor :key, :name, :shortcut, :type, :youtube_channels, :team, :locations, :aws, :settings
|
|
148
|
+
attr_accessor :key, :name, :shortcut, :type, :youtube_channels, :team, :git_remote, :locations, :aws, :settings
|
|
148
149
|
|
|
149
150
|
def initialize(key, data)
|
|
150
151
|
@key = key
|
|
@@ -153,6 +154,7 @@ module Appydave
|
|
|
153
154
|
@type = data['type'] || 'owned'
|
|
154
155
|
@youtube_channels = data['youtube_channels'] || []
|
|
155
156
|
@team = data['team'] || []
|
|
157
|
+
@git_remote = data['git_remote']
|
|
156
158
|
@locations = BrandLocation.new(data['locations'] || {})
|
|
157
159
|
@aws = BrandAws.new(data['aws'] || {})
|
|
158
160
|
@settings = BrandSettings.new(data['settings'] || {})
|
|
@@ -165,6 +167,7 @@ module Appydave
|
|
|
165
167
|
'type' => @type,
|
|
166
168
|
'youtube_channels' => @youtube_channels,
|
|
167
169
|
'team' => @team,
|
|
170
|
+
'git_remote' => @git_remote,
|
|
168
171
|
'locations' => @locations.to_h,
|
|
169
172
|
'aws' => @aws.to_h,
|
|
170
173
|
'settings' => @settings.to_h
|
|
@@ -43,6 +43,31 @@ module Appydave
|
|
|
43
43
|
path
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# Get git remote URL for a brand (with self-healing)
|
|
47
|
+
# @param brand_key [String] Brand key (e.g., 'appydave', 'voz')
|
|
48
|
+
# @return [String, nil] Git remote URL or nil if not a git repo
|
|
49
|
+
def git_remote(brand_key)
|
|
50
|
+
Appydave::Tools::Configuration::Config.configure
|
|
51
|
+
brands_config = Appydave::Tools::Configuration::Config.brands
|
|
52
|
+
brand_info = brands_config.get_brand(brand_key)
|
|
53
|
+
|
|
54
|
+
# 1. Check if git_remote is already configured
|
|
55
|
+
return brand_info.git_remote if brand_info.git_remote && !brand_info.git_remote.empty?
|
|
56
|
+
|
|
57
|
+
# 2. Try to infer from git command
|
|
58
|
+
brand_path_dir = brand_path(brand_key)
|
|
59
|
+
inferred_remote = infer_git_remote(brand_path_dir)
|
|
60
|
+
|
|
61
|
+
# 3. Auto-save if inferred successfully
|
|
62
|
+
if inferred_remote
|
|
63
|
+
brand_info.git_remote = inferred_remote
|
|
64
|
+
brands_config.set_brand(brand_info.key, brand_info)
|
|
65
|
+
brands_config.save
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
inferred_remote
|
|
69
|
+
end
|
|
70
|
+
|
|
46
71
|
# Expand brand shortcut to full brand name
|
|
47
72
|
# Reads from brands.json if available, falls back to hardcoded shortcuts
|
|
48
73
|
# @param shortcut [String] Brand shortcut (e.g., 'appydave', 'ad', 'APPYDAVE')
|
|
@@ -165,6 +190,19 @@ module Appydave
|
|
|
165
190
|
config.save
|
|
166
191
|
ERROR
|
|
167
192
|
end
|
|
193
|
+
|
|
194
|
+
# Infer git remote URL from git repository
|
|
195
|
+
# @param path [String] Path to git repository
|
|
196
|
+
# @return [String, nil] Remote URL or nil if not a git repo
|
|
197
|
+
def infer_git_remote(path)
|
|
198
|
+
return nil unless Dir.exist?(path)
|
|
199
|
+
|
|
200
|
+
# Try to get git remote URL
|
|
201
|
+
result = `git -C "#{path}" remote get-url origin 2>/dev/null`.strip
|
|
202
|
+
result.empty? ? nil : result
|
|
203
|
+
rescue StandardError
|
|
204
|
+
nil
|
|
205
|
+
end
|
|
168
206
|
end
|
|
169
207
|
end
|
|
170
208
|
end
|
|
@@ -157,6 +157,14 @@ module Appydave
|
|
|
157
157
|
'archived'
|
|
158
158
|
end
|
|
159
159
|
|
|
160
|
+
# Check S3 staging (only if local exists)
|
|
161
|
+
s3_staging_path = File.join(local_path, 's3-staging')
|
|
162
|
+
s3_exists = local_exists && Dir.exist?(s3_staging_path)
|
|
163
|
+
|
|
164
|
+
# Check for storyline.json
|
|
165
|
+
storyline_json_path = File.join(local_path, 'data', 'storyline.json')
|
|
166
|
+
has_storyline_json = local_exists && File.exist?(storyline_json_path)
|
|
167
|
+
|
|
160
168
|
# Check SSD (try both flat and range-based structures)
|
|
161
169
|
ssd_exists = if ssd_available
|
|
162
170
|
flat_ssd_path = File.join(ssd_backup, project_id)
|
|
@@ -168,11 +176,16 @@ module Appydave
|
|
|
168
176
|
|
|
169
177
|
{
|
|
170
178
|
id: project_id,
|
|
179
|
+
type: has_storyline_json ? 'storyline-app' : 'flivideo',
|
|
180
|
+
hasStorylineJson: has_storyline_json,
|
|
171
181
|
storage: {
|
|
172
182
|
ssd: {
|
|
173
183
|
exists: ssd_exists,
|
|
174
184
|
path: ssd_exists ? project_id : nil
|
|
175
185
|
},
|
|
186
|
+
s3: {
|
|
187
|
+
exists: s3_exists
|
|
188
|
+
},
|
|
176
189
|
local: {
|
|
177
190
|
exists: local_exists,
|
|
178
191
|
structure: structure,
|
data/package.json
CHANGED
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.
|
|
4
|
+
version: 0.19.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
|