From 4ecb9e0142f07f323db64da0fb9918aa7cb2f409 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Liechti Date: Mon, 26 Oct 2026 22:43:28 +0100 Subject: [PATCH 1/2] feat: add B-Mount Batteries, AC Power & Extension Cords and Tungsten categories Co-Authored-By: Claude Opus 5.5 --- ...000_add_bmount_and_ac_power_categories.php | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 db/migrations/20261026120000_add_bmount_and_ac_power_categories.php diff --git a/db/migrations/20261026120000_add_bmount_and_ac_power_categories.php b/db/migrations/20261026120000_add_bmount_and_ac_power_categories.php new file mode 100644 index 00000000..c10eee97 --- /dev/null +++ b/db/migrations/20261026120000_add_bmount_and_ac_power_categories.php @@ -0,0 +1,72 @@ + 'B-Mount Batteries', 'fontAwesome' => 'fas fa-car-battery', 'rank' => 395, 'group' => 7], + // Rank 245: after "Stands" (240), before "Accessories" (250) in "Lighting" (group 4). + ['name' => 'AC Power & Extension Cords', 'fontAwesome' => 'fas fa-plug', 'rank' => 245, 'group' => 4], + // Rank 215: after "Specialty LED" (210), before "Light modifiers" (220) in "Lighting" (group 4). + ['name' => 'Tungsten', 'fontAwesome' => 'fas fa-sun', 'rank' => 215, 'group' => 4], + ]; + + /** + * Adds "B-Mount Batteries" to the "Power" group, and "AC Power & Extension Cords" + * and "Tungsten" to the "Lighting" group, using the gaps left by the step-10 rank scheme from + * db/migrations/20260707120000_reorganize_asset_categories.php. + */ + public function up(): void + { + $preCountRow = $this->fetchRow("SELECT COUNT(*) AS c FROM assetCategories WHERE assetCategories_deleted = 0"); + $preCount = (int) $preCountRow['c']; + + foreach (self::CATEGORIES as $category) { + $escapedName = str_replace("'", "''", $category['name']); + $this->execute("INSERT INTO assetCategories + (assetCategories_name, assetCategories_fontAwesome, assetCategories_rank, assetCategoriesGroups_id, instances_id, assetCategories_deleted) + VALUES ('{$escapedName}', '{$category['fontAwesome']}', {$category['rank']}, {$category['group']}, NULL, 0);"); + $newId = (int) $this->getAdapter()->getConnection()->lastInsertId(); + if (!$newId) { + throw new Exception("AddBmountAndAcPowerCategories: failed to insert new category '{$category['name']}'"); + } + } + + $postCountRow = $this->fetchRow("SELECT COUNT(*) AS c FROM assetCategories WHERE assetCategories_deleted = 0"); + $postCount = (int) $postCountRow['c']; + $expected = $preCount + count(self::CATEGORIES); + if ($postCount !== $expected) { + throw new Exception("AddBmountAndAcPowerCategories: expected active category count to be {$expected}, found {$postCount}"); + } + } + + /** + * Aborts (does not delete) if any assetTypes have already been assigned to + * these categories, to avoid destroying user-created data on rollback. + */ + public function down(): void + { + $ids = []; + foreach (self::CATEGORIES as $category) { + $escapedName = str_replace("'", "''", $category['name']); + $row = $this->fetchRow("SELECT assetCategories_id FROM assetCategories WHERE assetCategories_name = '{$escapedName}' AND assetCategoriesGroups_id = {$category['group']} AND instances_id IS NULL AND assetCategories_deleted = 0"); + if (!$row) { + continue; + } + $id = (int) $row['assetCategories_id']; + + $usageRow = $this->fetchRow("SELECT COUNT(*) AS c FROM assetTypes WHERE assetCategories_id = {$id}"); + if ((int) $usageRow['c'] > 0) { + throw new Exception("AddBmountAndAcPowerCategories down(): category id {$id} has assetTypes referencing it - cannot safely delete. Reassign those asset types manually before rolling back."); + } + $ids[] = $id; + } + + foreach ($ids as $id) { + $this->execute("DELETE FROM assetCategories WHERE assetCategories_id = {$id};"); + } + } +} From dae63013e5fbbf5be6784f4576ba89d2bd281d67 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Liechti Date: Mon, 2 Nov 2026 14:56:43 +0100 Subject: [PATCH 2/2] docs: promote stage to production through PRs on our fork only Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a173ae84..d69cc048 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,10 +39,8 @@ git merge main # Resolve conflicts if any (our customizations may conflict with upstream changes) git push origin stage -# After testing on stage, update production -git checkout production -git merge stage -git push origin production +# After testing on stage, update production via a PR (see "Deploy to Production") +gh pr create --repo liechtjc/adam-rms --base production --head stage ``` **Important:** Do NOT automatically sync with upstream. Only pull updates when: @@ -86,13 +84,16 @@ git push origin stage - Test thoroughly before promoting to production #### Deploy to Production (Live) -Only after successful testing on stage: +Only after successful testing on stage, promote through a pull request `stage` → `production` on our fork: ```bash -git checkout production -git merge stage -git push origin production +gh pr create --repo liechtjc/adam-rms --base production --head stage \ + --title "Release: " --body "" +# Wait for CI (reviewdog, Docker build), then merge with a merge commit +gh pr merge --repo liechtjc/adam-rms --merge ``` -- Triggers automated deployment to production environment +- PRs are **only ever opened on `liechtjc/adam-rms`**: `gh` otherwise defaults to the upstream `adam-rms/adam-rms`. Always pass `--repo liechtjc/adam-rms` (and `gh repo set-default liechtjc/adam-rms` once per clone). Never open a PR, issue or comment on upstream. +- Feature branches still merge directly into `stage` (no PR). +- Merging does not deploy: redeploy production manually afterwards. ### Deployment Configuration - Docker compose files in `deploy/` directory @@ -111,7 +112,8 @@ git push origin production ⚠️ **ALL custom work happens on `stage`** or feature branches from `stage` ⚠️ **ALWAYS test on `stage` before promoting to `production`** ⚠️ **NEVER automatically sync with upstream** - only on explicit request -⚠️ **When syncing upstream, always test thoroughly on `stage` before `production`** +⚠️ **When syncing upstream, always test thoroughly on `stage` before `production`** +⚠️ **`production` only changes through a PR on `liechtjc/adam-rms`** - never push to it directly, never target the upstream repo ### Hotfix Workflow (Emergency Only) If production is broken and needs immediate fix: @@ -122,10 +124,12 @@ git checkout -b hotfix/critical-issue # Make fix and test git commit -m "Hotfix: critical issue" -# Merge to production +# Merge to production via a PR on our fork +git push origin hotfix/critical-issue +gh pr create --repo liechtjc/adam-rms --base production --head hotfix/critical-issue +gh pr merge --repo liechtjc/adam-rms --merge git checkout production -git merge hotfix/critical-issue -git push origin production +git pull origin production # Backport to stage git checkout stage