Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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: <summary>" --body "<what goes live>"
# Wait for CI (reviewdog, Docker build), then merge with a merge commit
gh pr merge <number> --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
Expand All @@ -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:
Expand All @@ -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 <number> --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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class AddBmountAndAcPowerCategories extends AbstractMigration
{
private const CATEGORIES = [
// Rank 395: after "V-Mount Batteries" (390), before "Small Format Batteries" (400) in "Power" (group 7).
['name' => '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.

Check warning on line 20 in db/migrations/20261026120000_add_bmount_and_ac_power_categories.php

View workflow job for this annotation

GitHub Actions / misspell

[misspell] db/migrations/20261026120000_add_bmount_and_ac_power_categories.php#L20

"reorganize" is a misspelling of "reorganise"
Raw output
./db/migrations/20261026120000_add_bmount_and_ac_power_categories.php:20:36: "reorganize" is a misspelling of "reorganise"
*/
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};");
}
}
}
Loading